<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Oscar</title>
    <description>The latest articles on Forem by Oscar (@kurealnum).</description>
    <link>https://forem.com/kurealnum</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1079248%2F6f43bf7a-eebe-414c-9404-4cc9765ac588.jpg</url>
      <title>Forem: Oscar</title>
      <link>https://forem.com/kurealnum</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kurealnum"/>
    <language>en</language>
    <item>
      <title>5 ways to destroy your Linux</title>
      <dc:creator>Oscar</dc:creator>
      <pubDate>Sat, 23 Aug 2025 13:06:55 +0000</pubDate>
      <link>https://forem.com/kurealnum/5-ways-to-destroy-your-linux-4bih</link>
      <guid>https://forem.com/kurealnum/5-ways-to-destroy-your-linux-4bih</guid>
      <description>&lt;p&gt;We all know that &lt;strong&gt;rm -rf /&lt;/strong&gt; can nuke your installation of Linux instantly, but what other ways can you break your Linux?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;By the way, I tested everything that I’m about to talk about on a fully installed Linux Mint VM (on Windows with Hyper-V, sadly, cause I can’t be bothered to set up a virtual machine on Arch).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you prefer to consume this article as a video, check this out!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/3-oEjTXqms0"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing your system time ⏰
&lt;/h2&gt;

&lt;p&gt;Yes, &lt;strong&gt;seriously&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now I hear you saying: what does that have to do with anything? And doesn’t my time sync automatically? Well, yes, and you do have to disable that. And consequently, this is pretty easy to fix. But it can still completely mess up your system. &lt;/p&gt;

&lt;p&gt;This is because a lot of encryption actually relies on time and timestamps, and if your computer is set to 2010, things like SSL, HTTPS, and a few other things simply won’t work.&lt;/p&gt;

&lt;p&gt;Anyways, disabling and changing the time is something that you can do on your Linux that shouldn't leave any permanent damage. But please do your own research before you try and break your wonderful installation of Arch. &lt;/p&gt;

&lt;h2&gt;
  
  
  Actually doing it 🔨
&lt;/h2&gt;

&lt;p&gt;You can disable ntp – this is the Network Time Protocol: the thing that makes sure your computer's time is correct – and then set the date and time like so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;service ntp stop

&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;'2014-12-25 12:34:56'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only other thing I want to mention here is the kinda interesting warning page by FireFox that pops up when you try to access the web over HTTPS after changing the system time. It provides a helluva lot of information, but surprisingly, it doesn’t mention Linux.&lt;/p&gt;

&lt;p&gt;You can check out the following URL if you’re on FireFox and curious: &lt;em&gt;chrome://global/content/neterror/supportpages/time-errors.html&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;But again, this is pretty easy to fix and it should even fix itself automatically if you don’t disable ntp!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qakxh59032ehoyredkm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qakxh59032ehoyredkm.png" alt="Screenshot of the first part of the above Firefox page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's destroy systemd! 🤯
&lt;/h2&gt;

&lt;p&gt;Now, for a more unique way of destroying your system, you can delete systemd targets. And if you’re not on systemd, you’re probably on OpenRC and the equivalent there is OpenRC runlevels.&lt;/p&gt;

&lt;p&gt;Now what are systemd targets and what do they do? Well, when your system starts, you don’t just want everything starting at once. That’s a bad idea for a lot of reasons. &lt;/p&gt;

&lt;p&gt;So what you do is you have these groups that start and stop different systemd unit files (things like .service files that you’ve probably seen before) at different times, and these groups are what systemd calls targets. And as I mentioned, they’re pretty synonymous with runlevels. So when we delete these targets and reboot, everything should break.&lt;/p&gt;

&lt;p&gt;One thing I should note is that I deleted the &lt;code&gt;/systemd/system/&lt;/code&gt; directories in lib and etc entirely. These include both the .service files that you’ve probably seen and interacted with before and the .target files – as well as all of the other types of systemd unit files. So we’re really just nuking systemd as a whole.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Yes, we could only delete the .target files... but what's the fun in that?&lt;/span&gt;
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /systemd/system/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now originally, I thought that this was just going to affect the reboot and startup, since that’s one of the only times that systemd is used. Right? Other than controlling daemons, of course. Well, kind of. Systemd is used, like, everywhere, apparently. I tried to reboot after deleting these files, and as both I and you probably guessed, I couldn’t. I couldn’t shut down either.&lt;/p&gt;

&lt;p&gt;Now for the fun part: I couldn’t even manually close the VM. This is the end of this section of the article and the end of this VM – literally, I couldn’t access the VM after this. &lt;/p&gt;

&lt;p&gt;If you’ve done this before and know what’s going on… let me know. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa6qvqjzo5vmwlshxwo5x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa6qvqjzo5vmwlshxwo5x.png" alt="HyperV logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m guessing it’s something with Hyper-V and how it’s shutting down Linux, but either way, systemd clearly plays a large role in everything on your system, and removing it has consequences. Which I should’ve expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;rm -rf/&lt;/code&gt; doesn't work? ⌨️
&lt;/h2&gt;

&lt;p&gt;Also while we’re here, &lt;code&gt;rm -rf /&lt;/code&gt; doesn’t actually do anything.&lt;/p&gt;

&lt;p&gt;It needs the &lt;code&gt;--no-preserve-root&lt;/code&gt; argument to actually do something (which for obvious reasons, is good, and including sudo is also probably a good option if you wanna get the full nuclear effect). But once you add that flag, everything, as you might imagine, just… implodes.&lt;/p&gt;

&lt;p&gt;You’ll see a few things that can’t be deleted for whatever reason, and nothing really seems to be out of order initially. That is until you start trying to run simple commands. And then when you click out of your terminal, things start magically disappearing. You can try and swap to a TTY, but nothing will happen. Everything is gone.&lt;/p&gt;

&lt;p&gt;When you reboot your machine you’ll be entered into grub rescue mode. Now I did try a few things to try and get out of rescue mode, but if you couldn’t already tell, we just launched a nuclear attack on our system and, well, it worked. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp3hf88gsm5dtn1s8fyig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp3hf88gsm5dtn1s8fyig.png" alt="Screenshot of the virtual machine in grub rescue mode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s almost no chance we’re getting anything back, not easily at least.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;:(){ :|:&amp;amp; };:&lt;/code&gt; 🍴
&lt;/h2&gt;

&lt;p&gt;Next, a fork bomb. You’ve probably seen this little monstrosity floating around the internet a few times.&lt;/p&gt;

&lt;p&gt;Now when I ran this, I was expecting everything to just freeze. But that didn’t happen. So what did? Well, the way this fork bomb works is by spawning a bunch of user processes. And there’s a few ways to limit user processes. Some of which I’m sure Linux Mint utilizes out of the box. &lt;/p&gt;

&lt;p&gt;Things like &lt;code&gt;ulimit&lt;/code&gt; or &lt;code&gt;limits.conf&lt;/code&gt;, and there’s also this thing called &lt;code&gt;PAM&lt;/code&gt; which stands for Pluggable Authentication Modules. So what actually ended up happening was nothing. After a quarter second or less, processes just stopped spawning.&lt;/p&gt;

&lt;p&gt;Now I did try and change &lt;code&gt;ulimit&lt;/code&gt; and &lt;code&gt;limits.conf&lt;/code&gt; and some stuff with &lt;code&gt;PAM&lt;/code&gt; but I never got that to work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ulimit&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; 1000000

&lt;span class="c"&gt;# These should be the same thing?&lt;/span&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;vim /etc/security/limits.conf &lt;span class="c"&gt;# etc...&lt;/span&gt;

&lt;span class="c"&gt;# pam_limits.so seems to be controlled by limits.conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Either way, due to the nature of how this fork bomb works, I’m pretty sure this wouldn’t have a permanent effect on your system. It just spawns a bunch of user processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The slow death... ☀️
&lt;/h2&gt;

&lt;p&gt;Ok, next is the slow death: deleting your package manager.&lt;/p&gt;

&lt;p&gt;This is actually kind of a pain to do. On Linux Mint, you have to use dpkg to remove apt, and as far as I’m aware, dpkg doesn’t actually do anything in terms of managing packages and software, so we don’t have to worry about removing &lt;em&gt;it&lt;/em&gt;. It just interacts with .deb files directly. And I have no idea how you would even begin removing something like pacman (Arch Linux). Maybe just delete a few folders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Forgot to record what I did here so... I'm kinda guessing&lt;/span&gt;

dpkg &lt;span class="nt"&gt;-P&lt;/span&gt; apt

&lt;span class="c"&gt;# or (?)&lt;/span&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get purge apt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anyways, this doesn’t have much of an effect initially. As you can see, I can still access the software that was already installed, which makes sense. We’re uninstalling the package manager, not the software itself. And you could totally still install software with curl or wget or git or something else. And I can reboot, and I can carry on with my normal life.&lt;/p&gt;

&lt;p&gt;But with all of the packages and software that most users have installed, this little change would slowly become catastrophic. For example, I have &lt;del&gt;1258&lt;/del&gt; 1247 (apparently) packages installed on Arch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pacman &lt;span class="nt"&gt;-Q&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="c"&gt;# 1247&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are constant updates and bug fixes being pushed to these packages, and not updating these packages and software on a regular basis leaves me open to more and more security vulnerabilities.&lt;/p&gt;

&lt;p&gt;And back to the distro that I was using to test this, Linux Mint, the update manager does naturally break when you uninstall apt. Which again, leaves us open to a continuously growing number of security vulnerabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can we fix it? 🏗️
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl1irosvfs8k14cyyjeh2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl1irosvfs8k14cyyjeh2.png" alt="Screenshot of Bob the Builder with the caption "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the question that I want to answer now is the following: can you always repair your system?&lt;/p&gt;

&lt;p&gt;While I was researching for this article, I realized that this is an absolutely mammoth question to ask with so many loose ends. But for the methods that we tried…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Systemd? You can probably get that back, just as long as you don’t have to reboot.&lt;/li&gt;
&lt;li&gt;Rm -rf /? Not without data recovery software. And even then, I’m not sure.&lt;/li&gt;
&lt;li&gt;Fork bomb? Not permanent.&lt;/li&gt;
&lt;li&gt;System time? Definitely fixable.&lt;/li&gt;
&lt;li&gt;Package manager? This… is interesting. Again, you can still download stuff after deleting your package manager, but I’m not sure if you can just drop a new version of apt into your system and have it work out of the box.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But what have you broken in your time using Linux? How many times have you reinstalled your distro? Let me know in the comments!&lt;/p&gt;

</description>
      <category>linux</category>
      <category>tutorial</category>
      <category>learning</category>
    </item>
    <item>
      <title>Stop Skimming Documentation</title>
      <dc:creator>Oscar</dc:creator>
      <pubDate>Fri, 25 Jul 2025 14:01:35 +0000</pubDate>
      <link>https://forem.com/kurealnum/stop-skimming-documentation-mbk</link>
      <guid>https://forem.com/kurealnum/stop-skimming-documentation-mbk</guid>
      <description>&lt;p&gt;Look, whether you’re a Linux user, a hardcore webdev, or even an IT technician, docs are a part of all of tech, but few people actually understand how to read them and more specifically absorb them.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you prefer to consume this article as a video, check this out!&lt;/em&gt;&lt;br&gt;
  &lt;iframe src="https://www.youtube.com/embed/0OKuvTkQYBs"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  But Oscar... 😕
&lt;/h2&gt;

&lt;p&gt;Now look, I hear you saying “but Oscar, I just read the whole Rust book” or “I just read what I can’t figure out on my own – people don’t know how to read documentation because these 20 page documents aren't necessary anymore”, and we’ll get to that in a second.&lt;/p&gt;

&lt;p&gt;Because yes, you’re right, documentation comes in many forms – READMEs, autodocs, and even videos (yes, Google makes videos sometimes instead of writing documentation). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8r9zv7kp2d78h5i1dtaf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8r9zv7kp2d78h5i1dtaf.png" alt="Screenshot of YouTube playlist for Googles videos documenting the YouTube API" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And as we get better at documenting our stuff, the need to have the patience to read through that 20 page document drastically decreases.&lt;/p&gt;

&lt;p&gt;But lemme ask you this (and I want you to actually, truly, think about this): are you reading the docs, or are you absorbing them? &lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is what you should be!&lt;/em&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdk41camu490m9tnbxofb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdk41camu490m9tnbxofb.gif" alt="SpongeBob absorbing water" width="220" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Can you recall everything that you just read about Rust or a Linux tool or some other technology that you’re learning? Well, probably not, and that’s normal, it’s not bad.&lt;/p&gt;

&lt;p&gt;But you probably can’t recall as much as you should be able to. See, a lot of us get sucked into this notion of reading without comprehending – and this brings me back to what I just said about reading docs instead of absorbing them.&lt;/p&gt;
&lt;h2&gt;
  
  
  Absorb it! 🌊
&lt;/h2&gt;

&lt;p&gt;Documentation, and really all forms of writing, are meant to be thought about and understood and comprehended, and when you just graze over a bit of writing, you’re not actually doing anything of that nature.&lt;/p&gt;

&lt;p&gt;And this makes sense. Your brain isn't like your Linux, unfortunately. You can’t just throw something at it and expect it to perfectly copy everything down – and you probably know this.&lt;/p&gt;

&lt;p&gt;Now there are a lot of strategies to mitigate this and we’ll talk about some in just a second. But first let’s talk about why – why should you care about being great at reading docs?&lt;/p&gt;

&lt;p&gt;And don’t worry, I’m not gonna try and sell you on this idea like your high school counselor telling you that reading is a skill that employers value.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39slqgv2kjtzz4zmf0xw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39slqgv2kjtzz4zmf0xw.jpg" alt="Highschool counselor's office" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me actually explain to you why you should learn how to actually absorb the information from the documentation that you read.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why? ❓
&lt;/h2&gt;

&lt;p&gt;Why, oh why, should you actually care about your documentation reading skills?&lt;/p&gt;
&lt;h3&gt;
  
  
  Because Linux 🐧
&lt;/h3&gt;

&lt;p&gt;Firstly, Linux (and any widely used technology that isn't dead simple).&lt;/p&gt;

&lt;p&gt;All of the try hard tech people, myself included, want or need to use Linux, but I don’t think a lot of people are aware of how much time it takes to learn to use Linux. And not just time, but effort. If you’re at all aware of how complicated Linux can get, you’ll know that there’s a lot of documentation reading involved.&lt;/p&gt;

&lt;p&gt;And well, if you skim everything that you try and read or watch, you’re gonna be forced to re-read a lot of what you just read. This is an energy drain: it’s demotivating, tiring, and it really kills the fun of Linux, or again, whatever tool you’re trying to use.&lt;/p&gt;

&lt;p&gt;I myself can confidently say that I’ve done this plenty of times: I skim over the initial manpage and then spend the next 20 minutes wondering why I don’t understand the CLI or program that I’m trying to use.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Just look at this documentation!&lt;/em&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxh35vknnn2h3uooeioa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxh35vknnn2h3uooeioa.png" alt="A very length bit of documentation about Futures and Async in Rust" width="800" height="819"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And as I mentioned, the Linux analogy can be applied to everything. Any language, framework, or library worth your time is going to have a considerable amount of documentation behind it, and if you can’t effectively absorb it, you’re kinda cooked.&lt;/p&gt;
&lt;h3&gt;
  
  
  Tutorial hell 🔥
&lt;/h3&gt;

&lt;p&gt;Secondly, this is the best way to get out of tutorial hell.&lt;/p&gt;

&lt;p&gt;If you’re not familiar with tutorial hell, it’s basically this state that every tech person reaches at some point. You’re advanced enough to do your own projects, but you’ve been following YouTube or FreeCodeCamp tutorials for so long that you don’t want to and don’t know how to build a project without a narrator guiding you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvcrurci1qgm7c53pryy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvcrurci1qgm7c53pryy.png" alt="Tutorial hell" width="699" height="661"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By the way, if you’re in this right now, let me know. I’m curious.&lt;/p&gt;

&lt;p&gt;Anyways, being fluent in docs is one of the best ways to help yourself out of this state, and even if you’re not actively in this state, you might find yourself or a friend there at some point, and this can really help.&lt;/p&gt;
&lt;h3&gt;
  
  
  For you! 🙂‍↕️
&lt;/h3&gt;

&lt;p&gt;Thirdly, this is kinda obvious: this is for your own benefit.&lt;/p&gt;

&lt;p&gt;The more efficiently you can absorb information, the more time you have to do the fun stuff, like coding, and getting stuck on really stupid bugs… that’s about it.&lt;/p&gt;

&lt;p&gt;Not only does this benefit you, but it benefits everyone around you.&lt;/p&gt;

&lt;p&gt;![A group of strongmen(&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o1ik0wat5ecueq3sqzhz.jpg" rel="noopener noreferrer"&gt;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o1ik0wat5ecueq3sqzhz.jpg&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Having someone that’s comfortable sitting down and absorbing a bunch of information out of a man page or something similar is extremely useful, and you’ll find that as you become a better information sponge, you also become a fountain of knowledge.&lt;/p&gt;
&lt;h2&gt;
  
  
  Ok, but how do I do this? 🫤
&lt;/h2&gt;

&lt;p&gt;Ok, finally, let’s talk about the how. This is the fun stuff.&lt;/p&gt;

&lt;p&gt;There are a lot of different ways to go about this, and if you wanna learn more about this, just look up “how tf do I learn from a textbook”.&lt;/p&gt;

&lt;p&gt;There are a lot of self taught academics out there that have dealt with the same quote unquote documentation problems that you might be dealing with, and thus will be able to offer some great advice and strategies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1pa2jcf41hie4blybicd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1pa2jcf41hie4blybicd.png" alt="Academics on YouTube" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But anyways, this is the big one: break it down and interact with the material. Let’s take the Rust book for example. I’m gonna start with this section about generics and &lt;a href="https://doc.rust-lang.org/book/ch10-00-generics.html" rel="noopener noreferrer"&gt;ohmygodthat’samassivewalloftext&lt;/a&gt;. But hold on; let’s not just read it. Let’s break this down.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4g2ubrjbk3ane99udz6c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4g2ubrjbk3ane99udz6c.png" alt="Section on Generics from the Rust Book" width="800" height="620"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  An example/usecase 🚩
&lt;/h3&gt;

&lt;p&gt;In this case we’re gonna try summarizing, but this could totally be adapted based on what I’m learning and what I’m doing. And by the way, please don’t use Chat Gippity to summarize – that completely misses the point of what we’re doing.&lt;/p&gt;

&lt;p&gt;Ok, first we’re told that generics are abstract tools. &lt;/p&gt;

&lt;p&gt;Then we’re told that functions and structs and a few other things can work with generics. &lt;/p&gt;

&lt;p&gt;There’s also this tidbit about traits and lifetimes but we’re gonna avoid that for now since it’s not really on topic. &lt;/p&gt;

&lt;p&gt;However, it should be noted that if the Rust book includes traits and lifetimes in a section about generics, we might also want to consider how they’re connected.&lt;/p&gt;

&lt;p&gt;Anyways, we broke down the problem. Now let’s interact with the material. When it comes to programming languages like this, I tend to enjoy just splatting something out into my editor and seeing if it's anywhere near right. Now you’ve probably seen generics before, but let’s act like you haven’t.&lt;/p&gt;
&lt;h3&gt;
  
  
  It's not working! 📉
&lt;/h3&gt;

&lt;p&gt;Ok, so I know generics are defined with the little pointy brackets anywhere we want multiple data types to be accepted into a parameter of some kind, so maybe when we define a type in a function parameter we can add a generic… oh that’s not working.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;my_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Let’s pause for a second. This is where we should probably stop, go back to the Rust book, and do what we just did again: break the problem down and interact with the material. Ok, our problem is that we don’t know the correct syntax for generics. Let’s look around… and oh, we just found a &lt;a href="https://doc.rust-lang.org/book/ch10-01-syntax.html" rel="noopener noreferrer"&gt;sample function definition with generics&lt;/a&gt;... and that makes a lot more sense!&lt;/p&gt;

&lt;p&gt;Ok, so it looks like we put the generic out here and then just include the type, T in this case, in the function parameter itself. And look at that, it works! (For the nerds in the room, yes, I know this isn’t a complete example (kinda))&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;my_func&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Hold on 🫷
&lt;/h2&gt;

&lt;p&gt;Now stay with me for a moment. You might say “oh but this doesn’t work for everything” but it does. This works for the documentation of anything – even the country I live in (and no, I’m not joking, you could totally do this with the law).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjs8eak6t3d5sg1f0pmk1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjs8eak6t3d5sg1f0pmk1.png" alt="Law for reckless driving in Virginia" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But look, my point is that you probably haven’t been doing this, but you definitely should be. Don’t just read a bunch of documentation and try to commit it to memory. Actually think about it and interact with it and all that fun stuff.&lt;/p&gt;
&lt;h2&gt;
  
  
  Bringing it full circle ➰
&lt;/h2&gt;

&lt;p&gt;And let’s bring this full circle. Remember at the start of the post how I noted that you probably can’t recall everything you just consumed and/or read.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I did, trust me!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And if you completely forgot about this, well, that proves my point. And if you didn’t, awesome. But let’s expand this idea to what we just did. Just think for a second, what did we learn about generics in Rust?&lt;/p&gt;
&lt;h3&gt;
  
  
  Actually learning 🏫
&lt;/h3&gt;

&lt;p&gt;If you said “it's for multiple data types and they go in pointy brackets outside of a function”, great. And don’t worry about the fact that this sounds wildly unorganized. That’s fine. &lt;/p&gt;

&lt;p&gt;What actually matters is that you understand it and can actually recall the information that you just absorbed.&lt;/p&gt;

&lt;p&gt;There’s a lot of science and psychology behind why this works, and for many reasons I’m not going to cover that on this channel. But if you’d like to know more, I do recommend Benjamin Keep. He makes quite a few very well researched videos on this stuff.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://www.youtube.com/@benjaminkeep" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fyt3.googleusercontent.com%2Fjtu9r9E5XYpyTpaS7iR-1eflz8E-wDafYi2AxxoJhUY4HtLM5qNER7QVQAbk6lvXhxAqtxEl%3Ds900-c-k-c0x00ffffff-no-rj" height="900" class="m-0" width="900"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://www.youtube.com/@benjaminkeep" rel="noopener noreferrer" class="c-link"&gt;
            Benjamin Keep, PhD, JD - YouTube
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Insights into learning, science, and society. For teachers, students and the science-curious.

I’m a cognitive scientist specializing in learning and instruction. I focus on how to develop expert reasoning skills and what the heck this beautiful, fascinating thing called science is.

You’ll also find videos on social science research methods and bigger picture stuff, like how social systems produce scientific knowledge and why that knowledge sometimes goes ignored or misapplied.

I have a PhD in learning sciences and technology design from Stanford and a law degree from Cornell.

You can read more at www.benjaminkeep.com and sign up to my email newsletter there.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fs%2Fdesktop%2Fb5d07f3d%2Fimg%2Ffavicon.ico" width="16" height="16"&gt;
          youtube.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  Another option 🛗
&lt;/h3&gt;

&lt;p&gt;But hold on, there is actually something else you can do here. If you’re still confused about something that you’re learning, try breaking it down and summarizing it, then building it back up in your own words.&lt;/p&gt;

&lt;p&gt;We can do that with our previous statement like so: “Rust generics allow for multiple data types to be handled through a singular function parameter, and the type parameter is defined in the angle brackets outside the function”. &lt;/p&gt;

&lt;p&gt;But do what makes you happy and do what makes you comfortable with the documentation that you just read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bad documentation 📜
&lt;/h2&gt;

&lt;p&gt;One other thing I wanna address is the following: how do you deal with bad documentation?&lt;/p&gt;

&lt;p&gt;This is adamantly the fun part – or, not so fun part, I guess. When you come across bad documentation that’s poorly written or lacks appropriate content, this whole “magical” strategy kinda breaks.&lt;/p&gt;

&lt;p&gt;Imagine if I couldn’t quickly find that sample function with generics from the Rust book. I would be dead in the water until I found either an example in the book or an example from relevant source code elsewhere.&lt;/p&gt;

&lt;p&gt;And I’m gonna be completely honest here: there’s no one right answer for how to handle atrocious documentation. That being said, I think the best thing to do and the natural response is to take to GitHub issues or the forums and ask a question.&lt;/p&gt;

&lt;p&gt;But usually, especially when we’re talking about Linux or some other well used technology, this isn’t a problem. And that’s kinda why I’m not addressing it so heavily.&lt;/p&gt;

&lt;h2&gt;
  
  
  Oh well. 🤷
&lt;/h2&gt;

&lt;p&gt;So yeah, you’ve probably been reading documentation incorrectly the entire time.&lt;/p&gt;

&lt;p&gt;But you might not agree with that, so let me know if you don’t, and let me know how you read documentation.&lt;/p&gt;

</description>
      <category>documentation</category>
      <category>programming</category>
      <category>productivity</category>
      <category>learning</category>
    </item>
    <item>
      <title>My Dad Left Windows For Linux. So Should Yours.</title>
      <dc:creator>Oscar</dc:creator>
      <pubDate>Sat, 07 Jun 2025 14:28:30 +0000</pubDate>
      <link>https://forem.com/kurealnum/my-dad-left-windows-for-linux-so-should-yours-4kpf</link>
      <guid>https://forem.com/kurealnum/my-dad-left-windows-for-linux-so-should-yours-4kpf</guid>
      <description>&lt;p&gt;With Windows getting progressively worse every day, Linux is slowly becoming a better option for the average user. However it’s still seen as a foreign operating system used only by techies, but I’m here to tell you that that’s not the case, and in fact, your dad could start comfortably using Linux right now. &lt;/p&gt;

&lt;p&gt;And in fact, my dad is running Linux Mint at the moment, and has been for the past 6 months.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you prefer to consume this article as a video, check this out!&lt;/em&gt;&lt;br&gt;
  &lt;iframe src="https://www.youtube.com/embed/nXaMfy5GYCo"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Why use something other than Windows? 🤔
&lt;/h1&gt;

&lt;p&gt;But before we talk about anything else, let’s talk about why your dad might even consider using something other than Windows.&lt;/p&gt;

&lt;p&gt;If you dual boot to Windows or still use Windows as your main OS, you’ve probably noticed a few forced updates here and there, as well as a few awkward bugs, ads, and more. &lt;/p&gt;

&lt;p&gt;For instance, my 2nd monitor randomly disconnects for no reason. Every time I go to open an app, I’m greeted with some random news that isn’t relevant to anything that I’m thinking about or doing. Oh and also Windows takes 30 seconds to connect to my internet on boot. &lt;/p&gt;

&lt;p&gt;Heck, even Pewdiepie uses Linux now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frh0te6y3i48twxd8cua3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frh0te6y3i48twxd8cua3.png" alt="Pewdiepie Linux Youtube Video"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Then why is Windows still the most widely used OS? 🥇
&lt;/h2&gt;

&lt;p&gt;As we all know, Windows offers a lot of features that make it very appealing to someone like your dad. It’s easy to learn, easy to understand, and generally simple, and that’s why it’s reigned as the most popular operating system for the last two decades.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr83zxeakuv4nurze3p4j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr83zxeakuv4nurze3p4j.png" alt="Windows homescreen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So Windows isn’t inherently bad, but it’s not good either. It’s just kinda “eh”. And even your dad will get bored of “eh” at some point.&lt;/p&gt;

&lt;h1&gt;
  
  
  Talking points to avoid ☣️
&lt;/h1&gt;

&lt;p&gt;Ok so with that, let’s talk about what I don’t wanna talk about concerning Linux. In other words, all the talking points that your dad doesn’t care about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Source Software (OSS) 💽
&lt;/h2&gt;

&lt;p&gt;Let’s face it. As much as you might be an open source advocate and as much as your laptop might look like a modern day Woodstock, your dad doesn’t care that Linux is open source. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76yqw6noc23jcs2xaj8v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76yqw6noc23jcs2xaj8v.png" alt="Laptop covered in stickers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nor does the average user. They also don’t care that the software they use on Linux is open source, or that their data isn’t being farmed as much as it is on Windows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Linux is SO much faster! (kinda) 🏃
&lt;/h2&gt;

&lt;p&gt;Secondly, another really common talking point that I see is that Linux is so much faster than Windows. But look, your dad doesn’t care that Linux is a bit faster than Windows (but they might care that it’s smoother – and we’ll talk about that later). That 10 second boot time? Forget about it. &lt;/p&gt;

&lt;p&gt;The average user wants a machine that consistently works in a reasonable amount of time, and Windows is reasonable enough for most. They don’t care if their machine takes 30 or 10 seconds to boot (that is, if they know what booting is), and they certainly don’t care if it takes a few seconds to connect to their wifi.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI 🤖
&lt;/h2&gt;

&lt;p&gt;And look, even when it comes to the AI slop that we all hate, we have to accept that the average user really doesn’t care about AI. As long as it’s reasonably helpful with summarizing that annoying coworker’s email, most users love it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I asked for an image that "exmplifies AI slop", but...&lt;/em&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc9k7b2nl60y1f508xozq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc9k7b2nl60y1f508xozq.png" alt="Chat GPT loading"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  The "average user" 😊
&lt;/h1&gt;

&lt;p&gt;But wait, hold on. What is this elusive average user (AKA your dad)?&lt;/p&gt;

&lt;p&gt;Think of your neighbor Mary that you see every now and then. She works at the local Walmart as an accountant. This is your average user. Not someone who spends 12 hours a day behind a screen on Arch Linux – just someone who wants their computer to work when it needs to work.&lt;/p&gt;

&lt;p&gt;I’m going to make a wild guess. If you’re reading this, you’re probably a bit of a tech nerd. And as a fellow tech nerd, I feel that we often forget that we are extremely disconnected from the average user of the software we create or promote. &lt;/p&gt;

&lt;p&gt;The average user:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does not use the command line&lt;/li&gt;
&lt;li&gt;Does not worry (too much) about what happens with their data&lt;/li&gt;
&lt;li&gt;Does not dream about what format the Session Description Protocol should be in based on RFC 4566 (true story, unfortunately)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So when we’re making these comparisons, we have to be wary that we are not the average user. And consequently, the average user is a lot more common than us, and when we’re trying to get them to do something, we have to market to them, not to us.&lt;/p&gt;

&lt;h1&gt;
  
  
  Linux itself 🤵 (tux, get it?)
&lt;/h1&gt;

&lt;p&gt;Ok. Linux itself. What’s better about it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Updates 🔃
&lt;/h2&gt;

&lt;p&gt;Firstly, as you’re probably aware of, updates on Linux (in this case Mint) are a lot easier to handle and manage than updates on Windows. There’s nothing forced, it’s just “oh, there’s a new update: you wanna get it”? This varies based on distro of course, but most average users would likely be using Mint or Ubuntu.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation ⏬
&lt;/h2&gt;

&lt;p&gt;Secondly, this is something that I don’t think a lot of people really think about. I’ve installed Windows on a brand new machine twice in the last 6 months. And both of those times, it has been a pain. However when I installed Mint on my dad’s computer, it was… painless. It was really simple, and it felt welcoming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ads 💲
&lt;/h2&gt;

&lt;p&gt;And finally, I know I already mentioned it, but ads. Because there are a LOT of ads on Windows. And guess what? Linux doesn’t really have ads.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkdsyn1l4401ktxkymn5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkdsyn1l4401ktxkymn5.png" alt="Ads on Windows"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Software on Windows/Linux 🖥️
&lt;/h1&gt;

&lt;p&gt;Ok, next, let’s talk about software on Windows compared to software on Linux.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Web browser 🕸️
&lt;/h2&gt;

&lt;p&gt;Now most people, both power users and the predefined average users, tend to do 90% of their work from their web browser. This is debatable, but with tools like Canva, Google Slides, and more, custom software for each and every program is becoming less and less common. And if it can’t go in a web browser, &lt;em&gt;insert profanity of your choice&lt;/em&gt; it, just make it an Electron app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generic software 🫥
&lt;/h2&gt;

&lt;p&gt;The one thing that we can talk about is WordPad and other generic Windows tools compared to something like LibreOffice or OpenOffice, and honestly, there’s not much to say here. I mean, OpenOffice can do what WordPad can do, and more. &lt;/p&gt;

&lt;p&gt;The full suite (which is free, of course) comes with software for documents, spreadsheets, and even presentations. In other words, everything the average user would need. It doesn’t look great, but that’s fine.&lt;/p&gt;

&lt;h1&gt;
  
  
  UI of Windows V.S. Linux 🏜️
&lt;/h1&gt;

&lt;p&gt;On that note, we should talk about the general UI of Linux in comparison to Windows&lt;/p&gt;

&lt;p&gt;This is something that I’m personally very picky about. Take a look at the file manager that comes with Linux Mint compared to the Windows file manager. Is it bad? Absolutely not. &lt;/p&gt;

&lt;p&gt;However, Windows certainly has a theme that makes everything feel really familiar to most people, so the change in UI is definitely a little bit weird. For obvious reasons, this change is also reflected elsewhere – the taskbar, the app menu, and really anything else.&lt;/p&gt;

&lt;p&gt;Past that, most of Linux (or in this case, Linux Mint) is pretty similar to Windows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgtp9634910z598gt9u83.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgtp9634910z598gt9u83.png" alt="Linux Mint Desktop"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can have an app menu just like Windows, a desktop just like Windows, and a settings menu just like Windows.&lt;/p&gt;

&lt;p&gt;Additionally, Zoom, most web browsers, and even specialized software like Unity or Unreal Engine works just fine on Linux.&lt;/p&gt;

&lt;h1&gt;
  
  
  A few things that &lt;em&gt;don't&lt;/em&gt; work ⛔
&lt;/h1&gt;

&lt;p&gt;There are a few things that don’t work though: Any Adobe products, and Google Drive. But let’s be honest – unless you’re a professional, there’s no reason you need to be using Photoshop when software such as Canva and Gimp exist. And not being able to sync files locally with Google Drive is a bit of a pain, but honestly, I don’t know that many people that actually do this.&lt;/p&gt;

&lt;h1&gt;
  
  
  But wait, actually... 🖐️
&lt;/h1&gt;

&lt;p&gt;Finally, I want to make one concession.&lt;/p&gt;

&lt;p&gt;You have to actually debug Linux. If you have a problem on Windows, the standard debugging process is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google it&lt;/li&gt;
&lt;li&gt;Restart computer&lt;/li&gt;
&lt;li&gt;Reinstall Windows &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have a problem on Linux, the standard debugging process is as follows: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dig through journalctl and dmesg to find the source of the error. &lt;/li&gt;
&lt;li&gt;Utilize grep if needed. &lt;/li&gt;
&lt;li&gt;Research and implement a fix to the problem, likely using the command line.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It should be noted that there are a lot of amazing resources out there that don’t involve scrolling through documentation, and it’s totally possible to solve some of the common problems of Linux with these resources – and in my opinion, these resources are a lot more useful than the “Have you tried unplugging it” forum posts you find on Microsoft Support.&lt;/p&gt;

&lt;p&gt;But that being said, as much as I despise the Windows debugging process, the fact of the matter is that it works for the majority of users.&lt;/p&gt;

&lt;h1&gt;
  
  
  Linux Mint &amp;amp; my dad
&lt;/h1&gt;

&lt;p&gt;And this is where I want to bring up my dad again.&lt;/p&gt;

&lt;p&gt;My dad has used Linux Mint for the last 6 months, and has had about 3 problems with it, 2 of which weren’t even directly Linux related (printer and browser issues). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvxregegmnufsyknkd9e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvxregegmnufsyknkd9e.png" alt="Hide the pain Harold"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, the one problem that was Linux related did involve me having to dig through journalctl and the logs of a few other programs. Is that the biggest issue in the world? Absolutely not. But it did mean that I had to take 30 minutes to figure out what was going on – something that probably would not have happened on Windows.&lt;/p&gt;

&lt;p&gt;But again, you must consider the alternatives. Either debug Linux every now and then, or reinstall Windows when it magically decides it’s time to implode.&lt;/p&gt;

&lt;h1&gt;
  
  
  To conclude...
&lt;/h1&gt;

&lt;p&gt;And that brings me to my conclusion. Because of what I just said, Linux will likely never be the de facto standard in comparison to Windows.&lt;/p&gt;

&lt;p&gt;In my opinion, there’s too much manual labor that goes into running a Linux distro and that completely hampers the marketability of Linux to the aforementioned average user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;However&lt;/strong&gt;, I do think that there is a strong possibility that a large portion of average users will gravitate towards Linux after getting tired of Windows. And those users will likely accept the fact that Linux takes a little bit more effort. In other words, Linux will reign superior not because it is better, but because Windows is worse.&lt;/p&gt;

&lt;p&gt;Take my dad for example. He spent two years with Windows constantly freezing or forcing updates (on an i5 11th Gen, mind you). So after a bit of discussion, he agreed that, while it might take a bit more effort, he would be happy to try and use Linux Mint. And 6 months in, he’s quite happy with it. He’s even installed some software through the command line.&lt;/p&gt;

&lt;p&gt;So to conclude, yes, Linux would work for your dad, and frankly, I think that your dad might start using Linux in the next few years – or at least consider it. But there is, as I’ll affectionately label, a bit of an acceptance process. &lt;/p&gt;

&lt;p&gt;Let me know what you think in the comments, and of course, as in years past, this will be the year of the Linux desktop.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>ux</category>
    </item>
    <item>
      <title>But how does torrenting actually work?</title>
      <dc:creator>Oscar</dc:creator>
      <pubDate>Fri, 16 May 2025 15:01:45 +0000</pubDate>
      <link>https://forem.com/kurealnum/but-how-does-torrenting-actually-work-5hlf</link>
      <guid>https://forem.com/kurealnum/but-how-does-torrenting-actually-work-5hlf</guid>
      <description>&lt;p&gt;&lt;em&gt;If you prefer to consume this article as a video, check this out!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/dleYEIHuTQo"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;README&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Also, a quick side note: this article intends to be fully language agnostic, meaning that we don't discuss &lt;em&gt;how&lt;/em&gt; to do something torrent-related in a certain language.&lt;/p&gt;

&lt;p&gt;Finally, if you're here because you're trying to implement a BitTorrent client yourself, you should probably check &lt;a href="https://app.eraser.io/workspace/sFlxPLTKk5SOtnMCSiVp?origin=share" rel="noopener noreferrer"&gt;this diagram out&lt;/a&gt; (you do have to make an account, sorry).&lt;/p&gt;

&lt;p&gt;You may have heard of BitTorrent or torrenting before, but you’ve probably never taken a look under the hood of how it all actually works. Torrenting is a surprisingly complex process, and in this article, we’re going to take a deep dive into the BitTorrent protocol and how it works.&lt;/p&gt;

&lt;h1&gt;
  
  
  WTF is BitTorrent? 🫤
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;If you're familiar, feel free to skip ahead&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Firstly, let’s talk about how it and torrenting works. So BitTorrent is, as you might know, decentralized, and downloads occur between these decentralized nodes called peers. Why? Well, you can commonly get much better download speeds if you download from multiple “servers”, or in this case peers, at the same time. By the way, these peers are also called seeders sometimes, as they “seed” the file that you’re looking to download.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ful1i1h8s6yqafvhvgshx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ful1i1h8s6yqafvhvgshx.png" alt="BitTorrent peer diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s walk through a demo: To get a list of peers to download from, you contact what’s called a Tracker. A Tracker maintains a list of all available peers for a certain file (and yes, this is kinda centralized, but you can do this in a decentralized way that we’ll talk about later) and it updates that list when a new peer is available. So, we request this list from a Tracker, and move on to the next step.&lt;/p&gt;

&lt;p&gt;With this list, we start requesting pieces of the file from the peers that we just acquired. These pieces are literal pieces – normally anywhere from 32 KB to 16 MB in size. Finally, when we have all the pieces, we assemble the file and disconnect from the network (actually, we can stay on the network and we’ll talk about that later.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh9o8xhusz04a6vigdrlc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh9o8xhusz04a6vigdrlc.png" alt="Mini BitTorrent diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The difference between torrent and torrent 👁️‍🗨️
&lt;/h2&gt;

&lt;p&gt;Let’s denote something important: .torrent refers to the torrent filetype that maintains all of the data needed to torrent, and BitTorrent refers to the protocol – that is, BitTorrent is the protocol that allows the process of torrenting to occur, and a .torrent file is just the file that contains the information needed to contact that tracker, get a list of peers, and contact those peers. More on that later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Some confusion 🙉
&lt;/h3&gt;

&lt;p&gt;But it gets even more confusing. BitTorrent also refers to the BitTorrent client, which is basically just a wrapper for the BitTorrent protocol. It has a GUI, and it allows you to easily torrent files. There are also a few other BitTorrent (again, referring to the protocol here) clients like uTorrent, that again, act as a wrapper for the BitTorrent protocol.&lt;/p&gt;

&lt;p&gt;And to make it even more confusing, BitTorrent can operate on multiple different protocols like UDP and TCP. And if that confuses you – well, I don’t blame you. This might help you understand what we’re talking about though (OSI model). This is the OSI model. You don’t need to be aware of it or how it works – just know that the physical layer refers to the literal cable transmitting data, the transport layer refers to stuff like TCP and UDP, and the application layer refers to protocols like HTTP.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgx71fhk9cfv34k3fah3a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgx71fhk9cfv34k3fah3a.png" alt="Small diagram of OSI model"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, the BitTorrent protocol operates by being boxed up and sent by TCP or UDP. There’s also this thing called uTP, which is the uTorrent Transport Protocol, but don’t worry about that.&lt;/p&gt;

&lt;h1&gt;
  
  
  Trackers 👈
&lt;/h1&gt;

&lt;p&gt;Ok, one final thing that is absolutely essential in understanding BitTorrent: everyone, except for the aforementioned Tracker servers, is considered to be equal – in other words, everyone carries their own weight. That means that if your client connects to a few peers and downloads a few files, your client is also expected to provide those files to other potential peers. Just keep that in mind as we walk through the lower levels of BitTorrent. And with that, we are also a peer in any BitTorrent network that we are connected to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trackers in depth 🫵
&lt;/h2&gt;

&lt;p&gt;Basics first. We start with either a torrent file or this thing called a magnet URI. We extract the relevant information, and make a request to the tracker to get a list of peers to start making requests to. Let’s take a deeper dive into this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjd4drgh7vf6ewlvpi3cw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjd4drgh7vf6ewlvpi3cw.png" alt="Torrent file to JSON diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I previously mentioned, this is the very first part of the BitTorrent protocol’s process of downloading the files: that is, getting a list of peers to download from.&lt;/p&gt;

&lt;h3&gt;
  
  
  What we need 🕴️
&lt;/h3&gt;

&lt;p&gt;Naturally, we need the IP address or URL of the tracker, as well as a few other things. This data can come in one of two forms, as just now mentioned: a .torrent file, or a magnet URI, which is somewhat like a .torrent file in the form of a URI – not exactly though.&lt;/p&gt;

&lt;p&gt;Now what are these “other things” that we need for the tracker, you ask? Well, we need this thing called an info hash and our peer id. The peer id is just our client's id on a given BitTorrent network (randomly generated), and the info hash is a SHA-1 hash of a part of the .torrent file. We need these so the tracker can identify what list of peers to return.&lt;/p&gt;

&lt;p&gt;If it’s a magnet URI, things are a bit simpler (or more complex, depending on how you look at it). A magnet URI has similar fields to a .torrent file, but not all fields are going to be there. If the address tracker (AKA the URL/IP to the tracker server) is there, we can just make a simple request to it, and move on with our lives. If it isn’t, we need to check if there is a list of peers to start with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgrdeytxtheys2v6a8l9u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgrdeytxtheys2v6a8l9u.png" alt="Diagram of extracting information from a magnet URI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is where that “more decentralized method of getting peers” that I previously mentioned plays in, and it’s called DHT. If we’re given a list of peers to start with, those peers may have another list of peers for us, and those peers might as well, and so on, so forth. In other words, if we don’t have a tracker, we can get peers from other peers. &lt;/p&gt;

&lt;p&gt;Finally, if none of those fields are present, we can attempt to download from the “acceptable source”. This is just a standard download, but it ensures that the user can at least download a file if nothing else works.&lt;/p&gt;

&lt;h3&gt;
  
  
  Peer(s)? 🫥
&lt;/h3&gt;

&lt;p&gt;Oh, and you may have noticed that I’ve been saying “peers”, plural. This is because there does need to be at least one peer, but we could be connected to 20, 50, or even more peers. This is incredibly important to note because throughout this article, I’ll be referring to just one peer for the sake of simplicity. Just know that this same process would occur for a lot of other peers.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Handshake 🤝
&lt;/h1&gt;

&lt;p&gt;Next, let’s take a look at the initial handshake made with a peer. A high level interpretation at this portion of the protocol would go something like this: &lt;/p&gt;

&lt;p&gt;We want to connect with a peer (also known as a seeder in this case, as they’re providing pieces of the file to us), so we say to the peer “hey, I have this file that I’d like to download. Can you send me your info hash so I can see if your file is intact?”. Then, if the info hash the peer sends back is correct, we can continue with the process. &lt;/p&gt;

&lt;p&gt;With that, let’s take a look at what the innards of this handshake actually look like.&lt;/p&gt;

&lt;p&gt;Recall that I’ll refer to this whole list of peers that we got from the tracker or the magnet URI as a singular peer for the sake of simplicity. To connect to a peer, we need to start by handshaking with them. &lt;/p&gt;

&lt;p&gt;Here’s what the header looks like. It’s 68 bytes long. It starts with the character 19 as a single byte, and the string “BitTorrent Protocol” as 19 bytes. There are then 8 bytes of padding, the 20 byte SHA-1 info hash that we just now discussed, and our 20 byte peer id.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqw69wpvn11hq6vhizze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqw69wpvn11hq6vhizze.png" alt="BitTorrent handshake"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We send this off to the peer, and await a response. The peer sends back what they think their SHA-1 hash is, and also ensures that everything else about the handshake is valid (is the right length, etc). &lt;/p&gt;

&lt;p&gt;We validate this handshake by checking if general features about the handshake are correct (again, is it the right length), and then ensuring the peer’s SHA-1 hash matches ours. If it doesn’t, the peers' pieces of the file might be corrupted, or simply incorrect. Whatever the issue may be, if the peer’s SHA-1 hash does not match ours, we sever the connection to that peer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fquncuqv7pa81up181qjc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fquncuqv7pa81up181qjc.png" alt="What makes a valid handshake chart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If everything looks right, we move on to the next step.&lt;/p&gt;

&lt;h1&gt;
  
  
  Actual messages 👋
&lt;/h1&gt;

&lt;p&gt;We’re able to start sending actual messages now. Do note that we’re also supposed to send keep-alive messages every two minutes.&lt;/p&gt;

&lt;p&gt;I would jump directly into what messages we’re sending, but we need to talk about the format first. On a fundamental level, there are 9 different messages that we can send to a peer. They are all denoted by the first byte: 0 through 8, inclusive. Actually, that’s a bit of a lie, because there’s 4 bytes of padding before the “type” byte. These 4 bytes are reserved for denoting the length of the message.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1xlx1gmzw1cyvxi9s6g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1xlx1gmzw1cyvxi9s6g.png" alt="Bittorrent message diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the “length” and the “type bytes, we can do… anything, at least anything that the BitTorrent protocol’s spec allows. You’ll see some examples of payloads soon, but the first 4 message types that we’ll talk about have no payload. They only represent the state of the peer. These types are choked, unchoked, interested, and not interested. The terminology behind these messages seems really weird initially, but trust me, it’ll make sense soon. For now, you can just think of these like messages that change the quote-unquote state of a peer (or us).&lt;/p&gt;

&lt;h2&gt;
  
  
  The messages ➡️
&lt;/h2&gt;

&lt;p&gt;The first message with a payload is the “have” message. The payload for this message is a single byte representing the index of the piece that the downloader just completed and checked the hash of. Recall that pieces are simply separate parts of a given file that the peer has.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3yzt690onrhyvcxl0xi2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3yzt690onrhyvcxl0xi2.png" alt="Have message diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next message with a payload is “bitfield”. This message is only ever sent as the first message, and the bytes of the bitfield correspond to the indices of pieces of the file (0-7, 8-15, and so on). Naturally, the payload is a bitfield (yes, a literal field of bits) with each index of the piece that the downloader has set to 1 and the rest set to 0. For example, this bitfield tells us that the peer has pieces 3, 5, and 6.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvf57asrg7rafnfrjliu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvf57asrg7rafnfrjliu.png" alt="Bitfield message diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next message is “request”. This message acts as… you guessed it, a request for a piece. It contains an index, begin, and length. The length refers to the length of the block we’re requesting and is usually a power of 2, the index refers to the literal index of the literal piece (think about the bitfield here), and begin refers to the offset on that piece. This is a good time to mention something weird: we don’t actually request pieces as an entire piece – we request each piece in blocks. To put it more aptly, we have the file, which is broken up into pieces, which are then requested in blocks. This is why we need an offset – to tell the peer or seeder where to start sending a block.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjepeth6wo0yxlpi020qe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjepeth6wo0yxlpi020qe.png" alt="Request message diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next message is “piece”. This message is a response to the request message. For instance, imagine we request a piece from a peer with the “request” message. That peer would respond with a “piece” message. A piece message contains an index, an offset, and a block: part of the actual piece we requested. The index is again the literal index of the piece, and the offset is copied over from the request message.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgf13mos6gvcnwcnsxiy6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgf13mos6gvcnwcnsxiy6.png" alt="Piece message diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final message is “cancel”. The use case of this message is a little bit weird, but we’ll get to that later – it’s called “endgame mode”, by the way. Naturally, it cancels an outstanding request, and it contains an index, an offset, and a length. We’ve already covered the index and the offset, and the length simply refers to the length field of the request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvcnbagrtco8tg74hchfb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvcnbagrtco8tg74hchfb.png" alt="Cancel message diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's send some messages! 🐱
&lt;/h2&gt;

&lt;p&gt;Finally, finally, finally, we are able to start sending messages!&lt;/p&gt;

&lt;p&gt;The first message that we send is a bitfield of the pieces we have, if we have any. Remember that in BitTorrent, everyone is treated as an equal, meaning that we’re expected to act as a seeder once we have pieces of the file. The first message that we receive is a “bitfield” message telling us what pieces the peer has. If the peer does not have any pieces, we can either sever the connection, or let the peer download files from us.&lt;/p&gt;

&lt;h1&gt;
  
  
  Choking 😮‍💨
&lt;/h1&gt;

&lt;p&gt;This is a great time to talk about choking – what it is, and why is it implemented in the BitTorrent protocol?&lt;/p&gt;

&lt;p&gt;The concept of choking is essentially an algorithm that helps block peers that are leeching – what is leeching, you ask? Well, it’s the inverse of seeding. Imagine if that peer we just talked about intentionally sent an empty bitfield. &lt;/p&gt;

&lt;p&gt;This would mean that they did not intend to contribute to the network at all, and instead wanted only to download a file. Naturally, leeching goes directly against the principle of equality in a BitTorrent network, and we need an efficient way to deal with this. Choking does exactly this, and it also helps with general performance and download speeds.&lt;/p&gt;

&lt;p&gt;All that being said, the choking algorithm is a bit too intricate for this article. Simply understand that it is a very useful tool for the BitTorrent protocol, and that any time you see a “choke”, “unchoke”, “interested”, or “not interested” message like you did earlier, we are just utilizing the choking algorithm.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You should really check out this video if you'd like a deep dive into choking&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/iognYJRzCE8"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Anyways, once we have sent and received any and all “bitfield” messages that we need, we can send an “interested” message and wait for and receive an “unchoke” message. If we have any files to supply, we will do the inverse.&lt;/p&gt;

&lt;h1&gt;
  
  
  Actually torrenting ⏬
&lt;/h1&gt;

&lt;p&gt;If these requests are successful, we can start working with the peer – both in uploading and downloading files.&lt;/p&gt;

&lt;p&gt;If the peer sent a bitfield of pieces, we can start requesting those pieces with a “request” peer message, and wait for a “piece” peer message in response to each of those requests. If the peer asks for any pieces, we can do the same. As long as we have a file to download or upload and no connections are unintentionally severed, we can stay connected to the peer (or again, multiple peers) and continue to upload or download files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu0cppat5ptozjiw6tf6o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu0cppat5ptozjiw6tf6o.png" alt="BitTorrent download algorithm"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now would be a great time to mention what endgame mode is. I think the BitTorrent docs sum this up pretty well, so I’ll quote them here: “When a download is almost complete, there's a tendency for the last few pieces to all be downloaded off a single hosed modem line, taking a very long time. To make sure the last few pieces come in quickly, once requests for all pieces a given downloader doesn't have yet are currently pending, it sends requests for everything to everyone it's downloading from. To keep this from becoming horribly inefficient, it sends cancels to everyone else every time a piece arrives.”&lt;/p&gt;

&lt;p&gt;In other words, the “cancel” message can be used to speed up downloads towards the end of the download process.&lt;/p&gt;

&lt;h1&gt;
  
  
  Cleaning up 🧹
&lt;/h1&gt;

&lt;p&gt;And finally, when we’re all done, we can sever the connection to the peer (or again, any and all peers that we are connected to). As far as I’m aware, there’s not much manual cleanup that we need to do with the BitTorrent protocol. It’s kinda just “ok, I’m done, bye”.&lt;/p&gt;

&lt;p&gt;Once we have all of the pieces, we combine them, and save the final file to disk.&lt;/p&gt;

&lt;p&gt;So with that, I hope this article was able to give you a better understanding of the BitTorrent Protocol. &lt;/p&gt;

&lt;p&gt;If there’s anything that I got wrong, please leave a comment and let me know. I'd be very happy to change anything that isn't correct.&lt;/p&gt;

&lt;h1&gt;
  
  
  Citations
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/44308457/confusion-around-bitfield-torrent" rel="noopener noreferrer"&gt;Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vimpunk/cratetorrent" rel="noopener noreferrer"&gt;CrateTorrent GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.jse.li/posts/torrent/" rel="noopener noreferrer"&gt;Understanding Torrent Protocol (jse.li blog)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://netfuture.ch/wp-content/uploads/2015/02/zink2012efficient.pdf" rel="noopener noreferrer"&gt;Efficient Peer-to-Peer File Sharing (PDF - Zink, 2012)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.bittorrent.org/beps/bep_0001.html" rel="noopener noreferrer"&gt;BEP 0001 - The BitTorrent Protocol Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.bittorrent.org/beps/bep_0003.html" rel="noopener noreferrer"&gt;BEP 0003 - The BitTorrent Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.bittorrent.org/beps/bep_0015.html" rel="noopener noreferrer"&gt;BEP 0015 - UDP Tracker Protocol for BitTorrent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.bittorrent.org/beps/bep_0023.html" rel="noopener noreferrer"&gt;BEP 0023 - Compact Tracker Responses&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.bittorrent.org/beps/bep_0029.html" rel="noopener noreferrer"&gt;BEP 0029 - uTorrent Transport Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.bittorrent.org/beps/bep_0053.html" rel="noopener noreferrer"&gt;BEP 0053 - Magnet URI v2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bittorrent.org/bittorrentecon.pdf" rel="noopener noreferrer"&gt;BitTorrent Economics Paper (PDF)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arpit.substack.com/p/the-choke-algorithm-that-powers-bittorrent" rel="noopener noreferrer"&gt;The Choke Algorithm That Powers BitTorrent (Substack)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/File:Tcp_state_diagram.png" rel="noopener noreferrer"&gt;TCP State Diagram (Wikipedia)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://app.codecrafters.io/courses/bittorrent/introduction" rel="noopener noreferrer"&gt;Codecrafters BitTorrent Course&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tutorial</category>
      <category>learning</category>
    </item>
    <item>
      <title>just got the two year badge, thanks dev.to :)</title>
      <dc:creator>Oscar</dc:creator>
      <pubDate>Sat, 10 May 2025 13:45:35 +0000</pubDate>
      <link>https://forem.com/kurealnum/just-got-the-two-year-badge-thanks-devto--2kbb</link>
      <guid>https://forem.com/kurealnum/just-got-the-two-year-badge-thanks-devto--2kbb</guid>
      <description></description>
      <category>gratitude</category>
      <category>community</category>
    </item>
    <item>
      <title>Just finished my new website, let me know what you think!! https://oscargaske.me/</title>
      <dc:creator>Oscar</dc:creator>
      <pubDate>Wed, 23 Apr 2025 22:43:05 +0000</pubDate>
      <link>https://forem.com/kurealnum/just-finished-my-new-website-let-me-know-what-you-thinkhttpsoscargaskeme-3p4b</link>
      <guid>https://forem.com/kurealnum/just-finished-my-new-website-let-me-know-what-you-thinkhttpsoscargaskeme-3p4b</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://oscargaske.me/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;oscargaske.me&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>website</category>
      <category>showdev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>I Made a TUI in C... and regretted it</title>
      <dc:creator>Oscar</dc:creator>
      <pubDate>Fri, 28 Mar 2025 14:53:08 +0000</pubDate>
      <link>https://forem.com/kurealnum/i-made-a-tui-in-c-and-regretted-it-2gh5</link>
      <guid>https://forem.com/kurealnum/i-made-a-tui-in-c-and-regretted-it-2gh5</guid>
      <description>&lt;p&gt;&lt;em&gt;Hey! You! If you would prefer to consume this article as a video, check this out!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/qgBG0t0sIDo"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  I know what you're thinking...
&lt;/h2&gt;

&lt;p&gt;You might've clicked on this article because there are so many better tools to build a TUI with. Or, you might say if I’m this desperate to do something with C, why not just make something simple, like a hashmap?&lt;/p&gt;

&lt;p&gt;But no, I have the compulsive need to go big or go home. I chose to (kind of) completely reimplement my favorite todo app, Todoist, in the terminal (and yes, I know this exists already). I hadn’t used C in about two years, and instead of getting warmed up to it with a few Easy Leetcode problems, I dove directly into the deep end.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the problem with C?
&lt;/h2&gt;

&lt;p&gt;It’s supposed to be this beautifully crafted, logical, low level programming language. Wait, hold on... there’s the problem. It’s a low level programming language. With C, this doesn’t just mean you control your program's memory, segfault after segfault after segfault. This means you control everything yourself.&lt;/p&gt;

&lt;p&gt;And yes, this applies to &lt;em&gt;everything&lt;/em&gt;. Wanna create a JSON object with one value in it?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// Ok, so maybe like 10 lines, if we're smart.&lt;/span&gt;
    &lt;span class="n"&gt;cJSON&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;monitor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cJSON_CreateObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;monitor&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cJSON_CreateString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Awesome 4K"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;cJSON_AddItemToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// From cJSON repo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome, that’ll be about 20 lines of code. This isn’t the fault of C, of course. This is how it’s meant to work. It just means that the most menial features take hours to complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  The libraries
&lt;/h2&gt;

&lt;p&gt;So with all that being said, let’s dig into the libraries used to make this TUI, what they do, and their biggest pain points.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ncurses
&lt;/h3&gt;

&lt;p&gt;If you’ve ever worked with C before, this is likely a name that scares you a little bit. Don’t get me wrong, Ncurses was amazing for its time… which was about 30 years ago. Every state change, every update to the UI, down to the exact row and column, is up to you to handle.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I recommend that you refer to the video for this part - &lt;a href="https://youtu.be/qgBG0t0sIDo?t=71" rel="noopener noreferrer"&gt;specifically 1:11&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And how exactly does it work, you ask? Well, there’s a lot going on behind the scenes that I certainly don’t understand, but generally speaking, Ncurses maintains something of a buffer that tracks the state of the currently used Ncurses Window – which in this case, refers to the Ncurses buffer, and not a window on your desktop – and then allows you to efficiently render and update that buffer and consequently re-render it to your terminal window by using &lt;code&gt;refresh()&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;ncurses.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;initscr&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;          &lt;span class="cm"&gt;/* Start curses mode          */&lt;/span&gt;
    &lt;span class="n"&gt;printw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World !!!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="cm"&gt;/* Print Hello World          */&lt;/span&gt;
    &lt;span class="n"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;          &lt;span class="cm"&gt;/* Print it on to the real screen */&lt;/span&gt;
    &lt;span class="n"&gt;getch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;            &lt;span class="cm"&gt;/* Wait for user input */&lt;/span&gt;
    &lt;span class="n"&gt;endwin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;           &lt;span class="cm"&gt;/* End curses mode        */&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Courtesy of Pradeep Padala&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ncurses also provides a bunch of out-of-the-box tools like menus and forms, similar to those that you would find in HTML and JavaScript. But as with anything else C related, you, the programmer, are expected to handle and deal with the guts of each of these tools. For instance, it takes about 50 lines of C and Ncurses to render a simple menu, and don’t even get me started on how one would actually make this menu reactive (well, do, because we’ll talk about this later).&lt;/p&gt;

&lt;h3&gt;
  
  
  cJSON
&lt;/h3&gt;

&lt;p&gt;The second big library that I used is cJSON. Now, I don’t know if this really falls under the definition of a “library” for C. It’s a single file, but it is about 5000 lines, and probably about 25% of my code involves cJSON, so I’m counting it as a quote-unquote important library.&lt;/p&gt;

&lt;h3&gt;
  
  
  Libcurl
&lt;/h3&gt;

&lt;p&gt;Now the other really big library that I used for this TUI is libcurl. As with Ncurses, it’s a wonderful library... that requires you to do everything yourself. Wanna make some headers?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;curl_slist&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;markCompleteHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;markCompleteHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="n"&gt;curl_slist_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;markCompleteHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;curlArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;markCompleteHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="n"&gt;curl_slist_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;markCompleteHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Content-Type: application/json"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;markCompleteHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="n"&gt;curl_slist_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;markCompleteHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Accept: application/json"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wonderful, that’ll be four function calls. Again, libcurl is a wonderful tool, it’s just a large step in a different direction from making an API request in something like Python or JavaScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation
&lt;/h3&gt;

&lt;p&gt;Finally, I want to mention documentation. If you’ve ever used a more modern language, framework, or library, which most of you probably have, you’re probably used to really beautiful, interactive, and consistently updated documentation. Take React for example. Almost every page has a small built-in tutorial as well as a font and font size that’s completely optimized for reading.&lt;/p&gt;

&lt;p&gt;But… that’s not the case for anything involved with Ncurses or Libcurl. Ncurses doesn’t even have a dedicated site. &lt;a href="https://tldp.org/HOWTO/NCURSES-Programming-HOWTO/" rel="noopener noreferrer"&gt;There’s one&lt;/a&gt; hosted by Pradeep Padala (I think, at least) &lt;a href="https://invisible-island.net/ncurses/" rel="noopener noreferrer"&gt;and another&lt;/a&gt; hosted by Thomas Dickey, who, as far as I know, is the current maintainer of Ncurses. While these sites are awesome, you’re really expected to work off of &lt;code&gt;man ncurses&lt;/code&gt; which is not wrong, so to speak, it’s just vastly different from what I’m used to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkd9ypjb1z3r37jm5b665.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkd9ypjb1z3r37jm5b665.png" alt="Screenshot of tldp.org/HOWTO/NCURSES-Programming-HOWTO/"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I said, you can also take a look at the Libcurl docs as another example. Again, there’s nothing inherently wrong with them – it’s just vastly different from what I’m used to. Lots of walls of text, and lots of “here’s some info, good luck” kind of documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why would I do this?
&lt;/h2&gt;

&lt;p&gt;Before we dive into the code itself, I wanna answer one really important question. &lt;em&gt;Why would I do this to myself?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’m sure this is something that’s come to your mind! As I previously mentioned, there are so, so, so many better options out there when it comes to making a TUI. You could do something in Rust, Python, or really any language, even JavaScript.&lt;/p&gt;

&lt;p&gt;Well, I’ve spent the last 5 months of my life working on &lt;a href="https://byeai.dev" rel="noopener noreferrer"&gt;byeAI&lt;/a&gt;, and after 500 commits, I feel… tired. There’s not really a better way to describe it. The last half of those commits didn’t involve me learning anything or doing anything new, just copying and pasting information from my brain. I wanted a break, and since I had a teensy tiny bit of previous experience with C, I wanted to do something with it.&lt;/p&gt;

&lt;p&gt;Also, I felt a little bit of “impostor syndrome” because a lot of the tools that I used to build byeAI were… kind of just handed to me. I don’t have to think about the nitty gritty details of security when I’m using React. I don’t have to write raw SQL when I’m using Django. I don’t even have to think about how to structure my code and project – Django is opinionated, in that it’s built to be used with the Model-View-Template architecture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftf026nplbchbibygy7zy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftf026nplbchbibygy7zy.png" alt="Diagram of Django's Model-View-Template architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anyways, all that is absolutely terrific and wonderful, and don’t get me wrong, these tools are the reason why we have what we have today. You can learn Django in a few months and whip up a delightful website without having to worry about how the heck your code handles an HTTP request, how it efficiently interacts with your database, or how the floobleflorp triggers a flooblegast. But sometimes you just wanna go back and prove to yourself that you can do what is being done for you, and while a TUI written in C certainly doesn’t do what Django does, it helps to get rid of that nasty, lurking feeling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;I’m a beginner when it comes to C. I’m sure there’s some awful code that could be completely improved or changed, and honestly, I’m sure that there are some memory leaks – and yes, I’m aware of Valgrind. So please don’t use this article to start learning C.&lt;/p&gt;

&lt;p&gt;Furthermore, I’ll cover the first part of the program in a lot of depth, but I won’t do so for the rest of the program. That would just be… way too much information. I just wanna cover the first few lines so you can get a good gist of how Ncurses and Libcurl is supposed to work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Todoist?
&lt;/h2&gt;

&lt;p&gt;I should also take a moment to explain how Todoist functions. Todoist has projects, and “todos”, also known as tasks or items, and those tasks can be put into projects. Those tasks can also have a due date, and if the due date is today, they’re also displayed in the “Today” tab.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first major step
&lt;/h2&gt;

&lt;p&gt;So with that, the first major step is to grab the list of projects from the API, display them to the user, and let the user choose what project they want to view specifically including the “Today” tab.&lt;/p&gt;

&lt;p&gt;Naturally, we start with the &lt;code&gt;main()&lt;/code&gt; function. We initialize Ncurses in this blob of code, and initialize a CURL handle with the &lt;code&gt;curl_easy_init()&lt;/code&gt; function. Because this is C, we have to check and C (pun intended) if the handle initialized properly. If it did, we continue with our program.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ncurses. stdscr acts as the "background", and everything else sits on top&lt;/span&gt;
  &lt;span class="c1"&gt;// of it.&lt;/span&gt;
  &lt;span class="n"&gt;initscr&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;noecho&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;printw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Loading current projects. Press q to exit.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;getmaxyx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stdscr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;keypad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stdscr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// curl&lt;/span&gt;
  &lt;span class="n"&gt;CURL&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;curl_easy_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;curl_global_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CURL_GLOBAL_DEFAULT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"curl didn't initalize correctly.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Carry on!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we get the auth token from the environment. This could certainly be done with a config file somewhere in the home directory or &lt;code&gt;~/.config&lt;/code&gt;, but as I’ll mention later, my primary objective with this program is simply to have a proof of concept and prove to myself that I can actually make a functional TUI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// ...&lt;/span&gt;
   &lt;span class="c1"&gt;// Get auth token from environment&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;authToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TODOIST_AUTH_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authToken&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;displayMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unable to find auth token. Press any button to end the "&lt;/span&gt;
                     &lt;span class="s"&gt;"program.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;curl_easy_cleanup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;curl_global_cleanup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;endwin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anyways, after we grab this authentication token, we slap it into a &lt;code&gt;curl_slist&lt;/code&gt;, which is basically just an HTTP header that we’ll add to the request in just a second.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;authHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;combineString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Authorization: Bearer "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;curl_slist&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;baseHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;baseHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;curl_slist_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authHeader&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we create the URL with one of my helper functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// ...&lt;/span&gt;
   &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;allProjectsUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;combineString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASE_REST_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"projects"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we get around to actually making the request. This is one of my structs called &lt;code&gt;curlArgs&lt;/code&gt;, and it just contains all of the essential stuff to make HTTP requests to a REST compliant API. If you’re unfamiliar, REST is kinda just a general API standard that generally supports &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt; requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// ...&lt;/span&gt;
   &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;curlArgs&lt;/span&gt; &lt;span class="n"&gt;allProjectsCurlArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;baseHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                           &lt;span class="n"&gt;allProjectsUrl&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
   &lt;span class="n"&gt;cJSON&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;projectsJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;makeRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allProjectsCurlArgs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;numOfProjects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cJSON_GetArraySize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectsJson&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, what about the &lt;code&gt;makeRequest()&lt;/code&gt; function? This is one of my functions, and it makes about 50 lines of code nice and reusable. If we take a look inside, we can see that it just sets a bunch of options of the curl handle (which is basically just options for the request we’re about to make), makes the request, and then handles the output.&lt;/p&gt;

&lt;p&gt;(If you'd like to see more, check out &lt;a href="https://github.com/kurealnum/todoist-terminal/blob/b00874a24dd8817434723cec7f084da1672542ed/main.c#L350" rel="noopener noreferrer"&gt;line 350&lt;/a&gt; in main.c)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="c1"&gt;// The options being set&lt;/span&gt;
  &lt;span class="n"&gt;curl_easy_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CURLOPT_WRITEFUNCTION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;curlWriteHelper&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;curl_easy_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CURLOPT_HTTPHEADER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;curlArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;curl_easy_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CURLOPT_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;curlArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;curl_easy_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CURLOPT_WRITEDATA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;requestData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;curl_easy_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CURLOPT_CUSTOMREQUEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;curlArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, I want to make one thing very clear while we’re here. We use the same curl handle throughout our entire program. That means that we need to manually set or clear every field every time we make a request, otherwise we’re going to end up with fields that simply aren’t what they should be. And yes, libcurl’s documentation does recommend this as it seems to be a lot more efficient to do this rather than create a new curl handle every single time we make a request.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The easy handle is used to hold and control a single network transfer. It is encouraged to reuse easy handles for repeated transfers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Anyways, back to the main function. Once we make the request and use cJSON to gather the data from it, we add one more field manually called &lt;code&gt;today&lt;/code&gt;. This is because of Todoist’s Today section that I find myself using like, 99% of the time, but their API doesn’t count it as an actual project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// ...&lt;/span&gt;
   &lt;span class="c1"&gt;// Adding a cJSON object so the user can also view active tasks (AKA the&lt;/span&gt;
    &lt;span class="c1"&gt;// today section)&lt;/span&gt;
    &lt;span class="n"&gt;cJSON&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cJSON_CreateObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cJSON_AddStringToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Today"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Adding a "fake" ID field to help with managing menu (see event loop ~20&lt;/span&gt;
    &lt;span class="c1"&gt;// lines down)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cJSON_AddStringToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"999"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;cJSON_AddItemToArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectsJson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have that, we render the projects menu. But don’t forget, simply rendering a menu doesn’t mean that the user can actually see the menu. We need to call &lt;code&gt;refresh()&lt;/code&gt; in order to actually display the menu to the screen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="n"&gt;MENU&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;projectsMenu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;renderMenuFromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectsJson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectsMenu&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Render&lt;/span&gt;
    &lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;set_menu_mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectsMenu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;post_menu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectsMenu&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// For free()-ing&lt;/span&gt;
    &lt;span class="n"&gt;ITEM&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;projectsItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;menu_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectsMenu&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, we start what I somewhat appropriately call an event loop. This while loop runs until the user presses q, which then causes the program to exit. Oh, and when that happens, there’s a whole bunch of cleanup that has to be done, of course.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// ...&lt;/span&gt;
   &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;getchChar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;getchChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getch&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sc"&gt;'q'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getchChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'j'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;menu_driver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectsMenu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REQ_DOWN_ITEM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KEY_UP&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;getchChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'k'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;menu_driver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectsMenu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REQ_UP_ITEM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let's see what we have!
&lt;/h2&gt;

&lt;p&gt;Oh… that’s it. Yeah, again, for the 30th time, we’re working with C here, which means that we’re doing the heavy lifting, so there’s not much to C yet. Anyways, what’s actually happening with this thing that I labeled as an “event loop”?&lt;/p&gt;

&lt;p&gt;Well, firstly, we of course need to have vim bindings! If the user presses k or j, the menu will go up or down, respectively. But that’s not the meat of the logic. The really important stuff happens when the user presses l.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;     &lt;span class="c1"&gt;// ...&lt;/span&gt;
     &lt;span class="err"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getchChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'l'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Find project ID, and call projectPanel with that project ID in a&lt;/span&gt;
        &lt;span class="c1"&gt;// curlArgs struct&lt;/span&gt;
        &lt;span class="n"&gt;cJSON&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;currentItemJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getCurrentItemJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectsMenu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;projectsJson&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentItemJson&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;displayMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="s"&gt;"Something went wrong when trying to access the current "&lt;/span&gt;
              &lt;span class="s"&gt;"item of the Ncurses menu. Press any key to quit."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="c1"&gt;//...&lt;/span&gt;
      &lt;span class="n"&gt;projectPanel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectPanelCurlArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the equivalent of opening a Todoist project and viewing the tasks inside it. Our code does this with Todoist’s API, and calls one really important function: &lt;code&gt;projectPanel&lt;/code&gt;. You can think of this as a copy of the &lt;code&gt;main()&lt;/code&gt; function. What it does is it makes a new Ncurses Window, which means that there’s now another buffer for Ncurses to write to. You can kinda think of this like a pop up, except for the fact that it takes up the entire screen.&lt;/p&gt;

&lt;p&gt;Then, as I just said, it does something very similar to what the &lt;code&gt;main()&lt;/code&gt; function does. It calls the Todoist API, and slaps the result into a menu. There’s a lot going on here, so I want to make a few notes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;projectPanel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;curlArgs&lt;/span&gt; &lt;span class="n"&gt;curlArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;PANEL&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;projectPanel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;WINDOW&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;projectWindow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Query for list of currently open tasks&lt;/span&gt;
  &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tasksUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;combineString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASE_REST_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"tasks"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;cJSON&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;unsortedTasksJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;makeRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curlArgs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Firstly, we’re going to need the data from each respective task, specifically the content, the id, and the priority number. We have access to this data when we call &lt;code&gt;renderMenuFromJson&lt;/code&gt;, which naturally renders a… menu from JSON. Then, in this function, we do a few different things. If there’s nothing in the given JSON, we create a so-called blank array of elements and return it. If there is something there, we call &lt;code&gt;createItemsFromJson&lt;/code&gt;, and return a menu with those items.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;MENU&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;renderMenuFromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cJSON&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;cJSON&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;currentTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;itemsLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cJSON_GetArraySize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// QOL&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemsLength&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ITEM&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;blankItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ITEM&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ITEM&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;blankItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NO_TASKS_TO_COMPLETE_MESSAGE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;blankItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ITEM&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;MENU&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;blankMenu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_menu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blankItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;blankMenu&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, in regard to the data from each respective task, we need to store it somewhere.&lt;/p&gt;

&lt;p&gt;So how do we do so? Well, when we create an Ncurses Menu item with &lt;code&gt;createItemsFromJson&lt;/code&gt;, we’re only given two fields to fill out. But, we can use the user pointer functions to store quote-unquote metadata for each Menu item. Now this took me &lt;em&gt;way&lt;/em&gt; too long to figure out, and I’m still not sure if I’m doing it correctly, but here’s how I’m doing it. You do also have to &lt;code&gt;free()&lt;/code&gt; this when you get rid of the menu, which isn’t that hard to do, but it should be noted.&lt;/p&gt;

&lt;p&gt;Ok, that was a lot. If you'd like to take a look at some visuals of how this works, check out &lt;a href="https://youtu.be/qgBG0t0sIDo?t=539" rel="noopener noreferrer"&gt;this part&lt;/a&gt; of the video.&lt;/p&gt;

&lt;p&gt;When the user presses l, the Todoist API is called, and relevant tasks are grabbed from the response. These tasks are then rendered in a panel (remember that a panel is basically just another Ncurses Window), and rendered into a Menu with &lt;code&gt;renderMenuFromJson&lt;/code&gt;, which calls &lt;code&gt;createItemsFromJson&lt;/code&gt;, which adds metadata to each item.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing single tasks
&lt;/h2&gt;

&lt;p&gt;We’re finally at the point where we can manage single tasks.&lt;/p&gt;

&lt;p&gt;We see an event loop here very similar to that of &lt;code&gt;main()&lt;/code&gt;, except for a few differences. If the user presses h or q, the event loop will end, closing the panel, and taking the user back to the main menu (AKA the projects menu). If they press p, the program will close the current task, which is basically just marking it as not due until the next day. If they press o, the task will be reopened – naturally, this is the inverse of closing a task. And finally, if they press d, the task will be permanently deleted, after confirmation from the user, of course.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="c1"&gt;// Event loop (ish?). Think of 'break' as going back to the projects menu.&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;getchChar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;getchChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getch&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sc"&gt;'q'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getchChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;KEY_DOWN&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;getchChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'j'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;menu_driver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasksMenu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REQ_DOWN_ITEM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getchChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;KEY_UP&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;getchChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'k'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;menu_driver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasksMenu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REQ_UP_ITEM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getchChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'h'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getchChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'p'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;ITEM&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;newItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;closeTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasksJson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tasksMenu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;curlArgs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newItems&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;setItemsAndRepostMenu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasksMenu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getchChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'o'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... and more&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a slight tangent, I want to take this time to again mention how much heavy lifting we’re doing here. Take a look at how the 10 lines of JSON for the API request for updating the TODO date of a task is made. It’s about… 70 lines, maybe. And really, that’s not even the worst of it. Just another glaring reminder of the niceties of modern languages (and as I try to keep in mind, say thank you open source maintainers. Most languages are open source, and programming would suck if the majority of languages were proprietary).&lt;/p&gt;

&lt;p&gt;Anyways, those functions are great and all, but they aren’t all that intricate. All that they involve is taking an item’s previously mentioned metadata, using it to call an API, and updating the items/menu if necessary.&lt;/p&gt;

&lt;p&gt;So let’s take a look at something that’s a bit more intricate: creating a task. We need to prompt the user for a task, and then create it, and manually append the new task onto the updated list of items.&lt;/p&gt;

&lt;p&gt;The prompting and appending are the fun parts here, so let’s take a deeper dive into that. Firstly, we need to get the input from the user. We can do so with a very nice out-of-the-box Ncurses tool called Fields. With this, we can pretty easily gather input data from the user. Do note that I’m using my own helper function here called &lt;code&gt;displayInputField&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;displayInputField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;infoText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Fields&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;lineLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lineLength&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;set_field_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;A_UNDERLINE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Forms&lt;/span&gt;
  &lt;span class="n"&gt;FORM&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Render&lt;/span&gt;
  &lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;post_form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It does exactly what the name suggests: displays an input field, gathers data, and returns the data when it’s done.&lt;/p&gt;

&lt;p&gt;So, we add a bit of styling to the form, and then render it. This is where things get a bit weird, and to be completely frank, I don’t think this is the intended way of doing things. If the user presses backspace, we update the field accordingly, and if the user presses a key other than enter, we again update the field accordingly. You’ll also notice that we maintain our own string of data. Once the user presses enter, we send this data back to the function it was originally called from, which is &lt;code&gt;createTask&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, as we’ve done about 93 times before, we make an API request and create this task.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// ...&lt;/span&gt;
   &lt;span class="n"&gt;cJSON&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;makeRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;createTaskCurlArgs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;displayMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s"&gt;"Something went wrong when making the request to close the "&lt;/span&gt;
          &lt;span class="s"&gt;"task. Press any key to return to the projects menu."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// tmp_uuid will be free later on&lt;/span&gt;
    &lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But now we have to manually update this list of items. Now, I think there might be a more efficient way to do this with &lt;code&gt;realloc()&lt;/code&gt;, but look, this is what I’m rolling with for the moment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// ...&lt;/span&gt;
   &lt;span class="n"&gt;ITEM&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;newItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;createItemsFromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasksJson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;curItemsLength&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;newItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;curItemsLength&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newTaskName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we have to do this funky little thing where we append a new item onto the end of this array, and append new meta data to said item. Finally, we’re able to return the new array of items, and update the menu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// ...&lt;/span&gt;
   &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;taskMetaData&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;newTaskMetaData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;taskMetaData&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;taskMetaData&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;newTaskMetaData&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newTaskName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="c1"&gt;// Get variables (redacted for sake of time)&lt;/span&gt;

    &lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmp_uuid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;idJson&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;valuestring&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;newTaskMetaData&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;newTaskMetaData&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;set_item_userptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;curItemsLength&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;newTaskMetaData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;newItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;curItemsLength&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ITEM&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;newItems&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  We're kinda done
&lt;/h2&gt;

&lt;p&gt;Now with that, we’re… kind of done. We’ve covered all of the really nasty and interesting stuff, and we’ve successfully created a working TUI, even though it might not look that good.&lt;/p&gt;

&lt;p&gt;On that note, what else can we do to make this look better? Well, there’s one main thing that comes to mind, and that’s color.&lt;/p&gt;

&lt;p&gt;Unfortunately, coloring an Ncurses menu is a pain, at best. And even then, you’re really not able to separately color that much, so I chose to leave everything unstyled and leave the customization up to the terminal emulator itself – in other words, transparency and stuff.&lt;/p&gt;

&lt;h2&gt;
  
  
  With all that being said...
&lt;/h2&gt;

&lt;p&gt;This program is not intended for general use.&lt;/p&gt;

&lt;p&gt;It’s a proof of concept, if anything. If you want a Todoist TUI, there are plenty of better options. There’s one written in Rust, which is actively maintained and sits at a healthy 1.5 thousand stars, and I’m sure you could find others if you look around enough.&lt;/p&gt;

&lt;p&gt;And with that being said, I don’t plan to improve this any further. If this is something that you, for some reason, really want to see grow, please feel free to fork it and develop it in your own time.&lt;/p&gt;

&lt;p&gt;peace&lt;/p&gt;

</description>
      <category>c</category>
      <category>ncurses</category>
      <category>programming</category>
      <category>coding</category>
    </item>
    <item>
      <title>An Actually Productive Arch Linux Setup</title>
      <dc:creator>Oscar</dc:creator>
      <pubDate>Fri, 28 Feb 2025 20:15:18 +0000</pubDate>
      <link>https://forem.com/kurealnum/an-actually-productive-arch-linux-setup-2d62</link>
      <guid>https://forem.com/kurealnum/an-actually-productive-arch-linux-setup-2d62</guid>
      <description>&lt;p&gt;&lt;em&gt;Psst! Hey you! If you'd like to watch a video instead, check this out:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/o03_cfOnl84"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I really like Linux.&lt;/p&gt;

&lt;p&gt;I love customizing my stuff, having full control over everything I use, using open source software, and of course, the moral superiority that comes with using Linux, but I didn’t start using it for any of those reasons&lt;/p&gt;

&lt;p&gt;You see, I started using Ubuntu a year or two ago because I despised having to work on Windows. Constant distractions, lack of customization, and… kinda slow sometimes. I also much prefer the Linux terminal/file system. I know that WSL exists, but it’s just not enough for me. &lt;/p&gt;

&lt;p&gt;Anyways, after a few months of using Ubuntu, I realized how much more productive I was. And so I decided to push that productivity to the max. I started messing around with KDE, which eventually led me to try i3. i3 led me to try a few other tools, and down the rabbit hole I went. A few months later, I swapped to Arch Linux and started using it 90% of the time, and dual booting to Windows only when necessary (which was really just for gaming). &lt;/p&gt;

&lt;h2&gt;
  
  
  A few things to note before we begin
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Firstly, if productivity is truly your only goal, then you should simply get a Mac, buy into everything in the Mac ecosystem, and use what your corporate overlords tell you to use. However, if you want to do things for a helluva lot cheaper (well, free – almost all of the software I use is open source -- so say &lt;code&gt;thank you&lt;/code&gt; to maintainers), then the few days that you’ll spend setting up Arch will be well worth it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Secondly, not everything has to be specifically about productivity. The best way to be productive is to enjoy your work, and there’s nothing wrong with having some fun configuring your tooling. A lot of the software that I mention in this article is more related to customization than it is productivity, and that’s fully intentional. I as a person am much happier using software that helps me be productive and that looks good.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And finally, as usual, this is not a tutorial. I’ll show you everything I use and why I use it, but I’m not gonna walk you through the setup of every single tool. But if you’d like me to, please leave a comment and let me know. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The really important stuff
&lt;/h2&gt;

&lt;p&gt;Firstly, a display manager. This is kinda just your lock screen, in a way. Because we’re going for maximum productivity, we’re going with an incredibly minimalistic, speedy, and vim-binding-supporting (is that a word?) display manager called ly. It does literally nothing but log you in, which is perfect for our usecase. As mentioned, it supports vim bindings, and you can also optionally use one of two effects: Matrix text, which looks like this (yes, the following pictures are taken IRL. It would be a pain to screenshot a TTY):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldbpxmeq5n6cylgo0sp3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldbpxmeq5n6cylgo0sp3.png" alt="IRL Picture of Matrix text effect" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;or the Doom fire effect, which looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyf30z79fiz0aihbdipqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyf30z79fiz0aihbdipqp.png" alt="IRL Picture of doom fire effect" width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, I don’t personally use these because my brain literally cannot handle this happening on three different monitors.&lt;/p&gt;

&lt;h3&gt;
  
  
  A window manager
&lt;/h3&gt;

&lt;p&gt;There are a lot of equally valid options for this, but I went with i3. It’s simple, it does exactly what it needs to do and nothing more, and it runs on X11. That might be a downside for some, but I generally prefer older tooling that works with almost any program you throw at it. That is, compared to Wayland, which, the last time I tried it, struggled to let me screenshare with Discord.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqtauk5g3wrkcz4gn9k5b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqtauk5g3wrkcz4gn9k5b.png" alt="i3 logo" width="323" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Compositor
&lt;/h3&gt;

&lt;p&gt;I spent a lot of time thinking about whether or not to include a compositor in this article, as it’s really not necessary for “pure productivity”. However, as I previously mentioned, not everything has to be specifically about productivity, and this is certainly a case where that idea applies. Now, there aren’t that many options for X11, but if you did choose to go the Wayland route, there are a LOT of options (like 20 or more, according to the Arch Linux wiki). With that being said, we’re going with picom. Picom is… awesome. It has very nice documentation, and most of the time, it… just works. It also supports all of the fancy things that you might want from a compositor: transparency, shadows, fading, opacity, and most importantly, animations (which were recently released in the V12 update).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Compression is due to Dev.to/AWS S3, nothing I can do about the blurriness here. Sorry!&lt;/em&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0od5xahg131xt2s46obq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0od5xahg131xt2s46obq.png" alt="Screenshot of desktop running i3 and picom" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Check out a non blurry screenshot &lt;a href="https://raw.githubusercontent.com/kurealnum/dotfiles/main/assets/theme.png" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Notification Daemon
&lt;/h3&gt;

&lt;p&gt;You might ask why I’m putting this in the “important stuff” section, and there’s one very good reason for that. Electron, a cross platform JavaScript framework that a lot of your favorite applications are built on (Discord, Spotify, VSCode, Figma, and more), implodes when it doesn’t have something to send notifications to. So, in other words, Electron based apps, like the ones that I just mentioned, instantly crash the moment a notification is sent if there’s no notification daemon installed. And don’t think that it won’t – I promise you, Electron will find a way to send some irrelevant notification. With all that being said, we’re going with dunst for our notification manager. It’s extremely customizable, and very easy to set up, which is why it deserves a spot in this article.&lt;/p&gt;

&lt;p&gt;Here's what dunst looks like for me. I'm using the Dracula theme with a few tweaks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe6791g9gxjebfmys21vq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe6791g9gxjebfmys21vq.png" alt="Dunst notification example" width="299" height="38"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  The less important stuff
&lt;/h2&gt;

&lt;p&gt;This is the section for stuff that I would recommend, but is totally not necessary. &lt;/p&gt;
&lt;h3&gt;
  
  
  A simple terminal emulator
&lt;/h3&gt;

&lt;p&gt;First up is a terminal emulator. We’ll go with Kitty here – it’s extremely fast, allows for rendering of images in the terminal which is really cool, multiple tabs, sessions, and a bunch of other customizable features that are generally effective at making you more productive.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://sw.kovidgoyal.net/kitty/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsw.kovidgoyal.net%2Fkitty%2F_images%2Fsocial_previews%2Fsummary_index_f767dd9c.png" height="418" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://sw.kovidgoyal.net/kitty/" rel="noopener noreferrer" class="c-link"&gt;
            kitty
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            If you live in the terminal, kitty is made for YOU! The fast, feature-rich, GPU based terminal emulator. Fast Uses GPU and SIMD vector CPU instructions for best in class performance, Uses threaded ...
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsw.kovidgoyal.net%2Fkitty%2F_static%2Fkitty.svg" width="256" height="256"&gt;
          sw.kovidgoyal.net
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&gt;

&lt;p&gt;A screenshot tool would be really useful for a number of reasons. As is the common trend in this article, there are a lot of different options here. However, I chose to go with a tool called Flameshot for 2 specific reasons. Firstly, it’s simple, yet it allows you to do a lot with your screenshots. And secondly, using Flameshot means that I don’t have to use anything from Gnome. But again, there are a lot of different options.&lt;/p&gt;

&lt;h3&gt;
  
  
  More efficient file browsing
&lt;/h3&gt;

&lt;p&gt;I briefly mentioned this in my previous article &lt;a href="https://dev.to/kurealnum/how-i-take-notes-with-neovim-9dd"&gt;about my note taking setup with Neovim&lt;/a&gt;, but I’ll cover it again here. I primarily use N^3 as my file browser because it’s super fast, supports Vim motions, and is super lightweight (no, really, it’s like 3 megabytes in total). However, I use the Thunar file browser from XFCE any time I need a GUI, like when I’m editing videos. N^3 sadly doesn’t allow you to drag and drop files. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;(This is not my video)&lt;/em&gt;&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://imgur.com/nnn-terminal-filebrowser-demo-kOld6HT" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FkOld6HT.gif%3Fnoredirect" height="337" class="m-0" width="600"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://imgur.com/nnn-terminal-filebrowser-demo-kOld6HT" rel="noopener noreferrer" class="c-link"&gt;
            nnn terminal filebrowser demo - GIF on Imgur
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Discover the magic of the internet at Imgur, a community powered entertainment destination. Lift your spirits with funny jokes, trending memes, entertaining gifs, inspiring stories, viral videos, and so much more from users like luukvbaal.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fs.imgur.com%2Fimages%2Ffavicon-32x32.png" width="32" height="32"&gt;
          imgur.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Blue light filtering
&lt;/h3&gt;

&lt;p&gt;The past few pieces of software that I’ve mentioned may be a little bit off topic, as in not directly related to productivity, but a blue light filter will usually help you sleep better, which directly impacts your productivity. It means you don’t have to be flashbanged (CSGO reference) every time you open up a tab that isn’t in dark mode at 10 o’clock at night. For instance, I’m writing this in Google Docs at the moment, and I’m only being mildly flashbanged because of my blue light filter. Anyways, on a Linux machine running X11, the best option for a blue light filter is a program called Redshift. As with most other things in this article, it’s very customizable, which is, y’know, awesome.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Your browser
&lt;/h3&gt;

&lt;p&gt;This is bit obvious, but I felt like it’s worth noting. A simple and effective web browser is really important to productivity, and that’s why I’d like to introduce Opera GX – no, I’m joking. In all seriousness, just use Firefox (or Firefox Developer Edition if you’re feeling fancy). It’s simple, and it works. I should note that I have run into one problem with it in the past: videos in AP Classroom won’t play correctly on Firefox. The only browser that works is Chrome. Thanks to the “non-profit” CollegeBoard for making a super accessible website! &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Is this related to tech? No, not at all. But I really don't like Collegeboard, and I felt like mentioning it&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiilvewc71ry37qf05htd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiilvewc71ry37qf05htd.png" alt="College board profits" width="702" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Coding
&lt;/h3&gt;

&lt;p&gt;And finally, if you’re gonna be coding, you should probably decide on your editor of choice. I’d of course vouch for Neovim, but it’s really up to you. VSCode, Vim, Emacs, Sublime Text, Visual Studio, IntelliJ, and the list goes on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customization.
&lt;/h2&gt;

&lt;p&gt;I should warn you that this section may be a little bit less focused on productivity, and it might turn into a yap session about how I customize my system. &lt;/p&gt;

&lt;h3&gt;
  
  
  Fonts
&lt;/h3&gt;

&lt;p&gt;Nevertheless, let's start with fonts. This may be surprising to you, but I’ve found that using the right font helps me focus and write faster – whether I’m writing code, a blog post, or the script for my next video. I use the Hack font for my terminal, and JetBrains Mono for the rest of my system. Also, when I’m not forced to write in Times New Roman, I use the font Proxima Nova at 11pt, normal weight. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd52v6vsampa5dd3t3osu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd52v6vsampa5dd3t3osu.png" alt="An example of Proxima Nova" width="298" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Wallpapers
&lt;/h3&gt;

&lt;p&gt;Next up, wallpapers. As I said, we’re getting off topic here. There are a bunch of wallpapers in my dotfiles, but there’s also a bunch of other places to find wallpapers online. You could check out the Arch Linux Discord server, or somewhere like r/unix-you-know-what. And don’t forget to use Google Image Search to search by images. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/kurealnum" rel="noopener noreferrer"&gt;
        kurealnum
      &lt;/a&gt; / &lt;a href="https://github.com/kurealnum/dotfiles" rel="noopener noreferrer"&gt;
        dotfiles
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      dotfiles!
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;kureal's dotfiles&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;This is heavily inspired by (or should I say 30% copied from) &lt;a href="https://github.com/jifuwater/Cattendeavour/blob/main/i3_pc/config" rel="noopener noreferrer"&gt;this rice.&lt;/a&gt;&lt;/p&gt;

&lt;img alt="Screenshot of rice" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fkurealnum%2Fdotfiles%2FHEAD%2F.%2Fassets%2Ftheme.png"&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Requirements&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;NerdFonts&lt;/li&gt;
&lt;li&gt;FiraCode Nerd Fonts&lt;/li&gt;
&lt;li&gt;JetBrains Mono (font)&lt;/li&gt;
&lt;li&gt;i3&lt;/li&gt;
&lt;li&gt;kitty (terminal)&lt;/li&gt;
&lt;li&gt;dunst&lt;/li&gt;
&lt;li&gt;feh&lt;/li&gt;
&lt;li&gt;lxappearance&lt;/li&gt;
&lt;li&gt;flameshot&lt;/li&gt;
&lt;li&gt;rose-pine-gtk&lt;/li&gt;
&lt;li&gt;picom&lt;/li&gt;
&lt;li&gt;nnn (not that, the file manager)&lt;/li&gt;
&lt;li&gt;redshift&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are likely more requirements that I'm forgetting about.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;General info&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Using pipewire and pavucontrol for audio management&lt;/li&gt;
&lt;li&gt;Using nmtui for network control&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/kurealnum/dotfiles" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h3&gt;
  
  
  An unnecessary wallpaper switcher/image viewer
&lt;/h3&gt;

&lt;p&gt;One thing that would actually kinda help (ok, I'm stretching it a bit here) in terms of productivity is being able to switch and view all of your wallpapers without having to use a silly GUI. For this, we’re going to use feh. It’s dead simple – just type &lt;code&gt;feh –bg-scale name&lt;/code&gt;. You can even add this to your i3 config if you like. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F56drmbeh5k25mzpjocdz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F56drmbeh5k25mzpjocdz.png" alt="feh example" width="748" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Theme manager
&lt;/h3&gt;

&lt;p&gt;And one more thing to mention in the name of customization: a simple GTK theme manager. There’s a lot of software that uses GTK, so being able to easily swap themes is, y’know, nice. I just use lxappearance – again, it’s simple, and it does exactly what it needs to do. &lt;/p&gt;

&lt;h2&gt;
  
  
  How do I maintain all of this?
&lt;/h2&gt;

&lt;p&gt;You may have heard of the phrase “system maintenance”, and if you’re running a distro like Arch, you definitely should’ve heard this phrase. All it really means is take care of your stuff. &lt;/p&gt;

&lt;p&gt;For this, I use a script that I stole from &lt;a href="https://fernandocejas.com/blog/engineering/2022-03-30-arch-linux-system-maintance/" rel="noopener noreferrer"&gt;this guy&lt;/a&gt;. Again, to be completely clear, this is not my code. I just put in a script, and accredited the guy who actually wrote it. I run this once every month, then restart my system to make sure everything is still running properly. You can find this script in my &lt;a href="https://github.com/kurealnum/dotfiles" rel="noopener noreferrer"&gt;dotfiles&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Please don't run this if you don't understand what it does!&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Updating system"&lt;/span&gt;
yay &lt;span class="nt"&gt;-Syu&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Clearing pacman cache"&lt;/span&gt;
&lt;span class="nv"&gt;pacman_cache_space_used&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; /var/cache/pacman/pkg/&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
paccache &lt;span class="nt"&gt;-r&lt;/span&gt; 
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Space saved: &lt;/span&gt;&lt;span class="nv"&gt;$pacman_cache_space_used&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing orphan packages"&lt;/span&gt;
yay &lt;span class="nt"&gt;-Qdtq&lt;/span&gt; | yay &lt;span class="nt"&gt;-Rns&lt;/span&gt; -

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Clearing ~/.cache"&lt;/span&gt;
&lt;span class="nv"&gt;home_cache_used&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/.cache&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/.cache/
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Spaced saved: &lt;/span&gt;&lt;span class="nv"&gt;$home_cache_used&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Clearing system logs"&lt;/span&gt;
journalctl &lt;span class="nt"&gt;--vacuum-time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;7d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That being said, this is definitely the bare minimum. Consider thinking about what else might need to be maintained. And if you have a bunch of configs that need to be maintained and struggle with a tiny bit of configuration addiction, start a maintenance schedule.&lt;/p&gt;

&lt;p&gt;For instance, I spend three months thinking about changes that I want to make to my system and to my Neovim config, and then I make all of those changes in one day. That way, I don’t constantly spend a bunch of time obsessing over perfection.&lt;/p&gt;

&lt;p&gt;Also, again, as with any script that you run, please read through it and make sure you understand what it’s doing before you run it. &lt;/p&gt;

&lt;h2&gt;
  
  
  I'd like to leave you on this note
&lt;/h2&gt;

&lt;p&gt;Perhaps, in the pursuit of maximum productivity, we’ve lost touch with what it means to be truly productive. Perhaps all of this configuration is a waste of time, and perhaps it is better to accept the fact that some software simply isn’t perfect, and perhaps we should all just cope a little bit. &lt;/p&gt;

&lt;p&gt;So with that being said, I’ll be completely uninstalling Arch Linux tomorrow and purchasing a Mac instead. May my corporate overlords have mercy on me. &lt;/p&gt;

</description>
      <category>archlinux</category>
      <category>linux</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I take notes with Neovim</title>
      <dc:creator>Oscar</dc:creator>
      <pubDate>Tue, 04 Feb 2025 19:47:08 +0000</pubDate>
      <link>https://forem.com/kurealnum/how-i-take-notes-with-neovim-9dd</link>
      <guid>https://forem.com/kurealnum/how-i-take-notes-with-neovim-9dd</guid>
      <description>&lt;p&gt;Firstly, I posted a video about this on my Youtube channel a few months ago, but I plan to go a lot more in depth here. Secondly, this is not a “how to” kind of article – I’ll show you the tools I use and why I use them, but not specifically how to set them up. There’s plenty of documentation out there for that!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hey! You! If you aren't a big fan of reading an article this long, you can check out the video here:&lt;/em&gt;&lt;br&gt;
  &lt;iframe src="https://www.youtube.com/embed/Mkno_8jnDdA"&gt;
  &lt;/iframe&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  What this setup needs to do 🛠️
&lt;/h2&gt;

&lt;p&gt;So what do I want out of my note taking setup? Well, in a general sense, I need to be able to quickly edit and view markdown and LaTeX files, and also transition between file types. &lt;/p&gt;

&lt;p&gt;And why am I changing my note taking setup, you ask? Well, I used Obsidian for quite a while, but it feels clunky and slow (probably because it’s another Electron based program, ew), and it doesn’t allow me to work as efficiently as I’d like. &lt;/p&gt;

&lt;p&gt;For instance, the search function in Obsidian sometimes just… bugs out? For example, using ripgrep in Neovim to search for “token” in my notes yields 7 results, but using the search function in Obsidian yields… 8 results? Even though there are only 7 results shown. It’s also kinda slow. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fimp0l8pe5nogw13dw9pn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fimp0l8pe5nogw13dw9pn.png" alt="Screenshot of Obsidian" width="243" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So now we know why I’m changing my note taking setup… but what does it need to do? That’s a really broad question that I kinda already answered, so let’s try and narrow it down a little bit. It needs to view Markdown and LaTeX files (plus LaTeX can compile to a PDF, so we need a PDF viewer as well), and as I mentioned, I’ve been taking notes in Obsidian, which takes notes in Markdown. Thus, because I want to take notes for Maths in LaTeX, I need to be able to transition between Markdown -&amp;gt; LaTeX -&amp;gt; PDF and LaTeX -&amp;gt; Markdown. These transitions might seem a little bit weird, but it’ll all make sense at the end of this article, I promise! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0l17u23kqgxxf2bylaxo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0l17u23kqgxxf2bylaxo.png" alt="Transition map" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also need to edit both Markdown and LaTeX files really fast. One of my biggest problems with Obsidian is that it’s slow as anything, so I want to completely fix that. Here’s what I’ve used to solve this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vim motions &lt;/li&gt;
&lt;li&gt;Snippets&lt;/li&gt;
&lt;li&gt;A fuzzy finder&lt;/li&gt;
&lt;li&gt;A better file manager than netrw (Neovim’s built in file manager that I love dearly) might be nice. &lt;/li&gt;
&lt;li&gt;Spellchecker&lt;/li&gt;
&lt;li&gt;Integrated terminal &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s get into the specifics.&lt;/p&gt;
&lt;h2&gt;
  
  
  Viewing Markdown and LaTeX files 👀
&lt;/h2&gt;

&lt;p&gt;…starting with viewing Markdown and LaTeX files. Neovim can of course open Markdown and LaTeX files, but it would be nice if they looked a bit better. Here’s what we’ll be using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Treesitter for syntax highlighting. 99% of configs have this already, I just thought I should mention it.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"lazy"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="n"&gt;spec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"nvim-treesitter/nvim-treesitter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Image.nvim for viewing images in the terminal. How cool is that? Although you do need to be using the Kitty terminal, and it’s a 
little bit… silly at times. We also need to get the file path for those images, and I had to set up a function that returns the correct file path for image.nvim. I know I said that this wasn’t intended to be a tutorial, but I felt that this was important to mention.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"lazy"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="n"&gt;spec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"3rd/image.nvim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Markview for Markdown. This just makes Markdown look a lot better. Also, I recently learned that this plugin has been almost entirely developed on a phone. You can read more about that &lt;a href="https://dev.to/kurealnum/this-developer-created-a-2k-star-repo-from-his-phone-57jj"&gt;here&lt;/a&gt;.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"lazy"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;spec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;"OXY2DEV/markview.nvim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;lazy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"nvim-treesitter/nvim-treesitter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s2"&gt;"nvim-tree/nvim-web-devicons"&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Vimtex for LaTeX. VimTex is incredibly useful for a lot of things, not just viewing LaTeX files. We’ll get to that later though. VimTex provides optional syntax highlighting  – although we already have treesitter for that. In order to appropriately view LaTeX files, I do need to view the rendered output of LaTeX – a PDF (more on that later), so I need a PDF viewer. I could just use Firefox, but I want to use something a bit more dedicated. Thus, for my PDF viewer, I went with Zathura. It’s nothing fancy – it supports Vim bindings, and… yeah, it views PDFs.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"lazy"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;spec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;"lervag/vimtex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;lazy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- we don't want to lazy load VimTeX&lt;/span&gt;
                &lt;span class="c1"&gt;-- tag = "v2.15", -- uncomment to pin to a specific release&lt;/span&gt;
                &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="c1"&gt;-- VimTeX configuration goes here, e.g.&lt;/span&gt;
                    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vimtex_view_method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"zathura"&lt;/span&gt;
                    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vimtex_view_forward_search_on_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
                    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vimtex_compiler_latexmk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;aux_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/home/oscar/.texfiles/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;out_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/home/oscar/.texfiles/"&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;end&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Transitioning between different file types ↔️
&lt;/h2&gt;

&lt;p&gt;As I previously mentioned, I originally took all of my notes in Obsidian, which takes notes in Markdown. However, I want to take notes for Maths in LaTeX, and notes for everything else in Markdown, so I’m going to want to transition all of my Math Markdown notes to LaTeX. Before I get any further, I want to mention that you can do all of this with a tool called pandoc. &lt;/p&gt;

&lt;p&gt;I was going to write a script to convert all of the Markdown files in my Maths folder to LaTeX, but I’m not great at bash, so I decided that I’d rather just convert them on the fly with this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pandoc &lt;span class="nt"&gt;-f&lt;/span&gt; markdown &lt;span class="nt"&gt;-t&lt;/span&gt; latex &lt;span class="nt"&gt;-s&lt;/span&gt; myfile.md &lt;span class="nt"&gt;-o&lt;/span&gt; myfile.tex 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I should note that pandoc does produce a significant amount of header in LaTeX (~40 lines). This doesn’t affect how the file looks though. Also, if you want to include images in your LaTeX files, you need to use the graphicx package and specify a file path for it like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\usepackage&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;graphicx&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;\graphicspath&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;/home/oscar/obsidian-notes/Images/&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I did mention that I might want to convert from LaTeX back to Markdown. This is because I do use Windows about 10% of the time, and I don’t have a purpose built LaTeX viewer on Windows. So, on the off chance that I need to take notes on Windows, I want to be able to do so without having to use an online editor like OverLeaf.&lt;/p&gt;

&lt;p&gt;So that gets us between Markdown and LaTeX (and vice versa). How about LaTeX to PDF? Well, this is where Vimtex really shines. You can just tell it what PDF viewer you want to use, hit “\ll”, and wait about a second for it to render.&lt;/p&gt;

&lt;p&gt;However, the amount of things you need to configure or install for this process are a little bit obnoxious. Here’s the list off the top of my head — there are likely more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;latexmk - the tool that Vimtex uses to compile LaTeX files to PDFS
disabling default for forward search in Zathura - if you don’t disable this, you’ll get a bunch of green highlights – kinda annoying &lt;/li&gt;
&lt;li&gt;Zathura itself - this config is pretty simple, I just feel like it’s not that well documented&lt;/li&gt;
&lt;li&gt;the output directory for latexmk, so you don’t get a few dozen random-ish files littering your directory. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Editing both Markdown and LaTeX files REALLY fast ✏️
&lt;/h2&gt;

&lt;p&gt;Finally, editing both Markdown and LaTeX files really fast. There’s not much to say about Markdown. I have a few little keybinds for general use, but at the end of the day… it’s just Markdown. Nothing fancy.&lt;/p&gt;

&lt;p&gt;On the contrary, VimTex is incredibly useful for LaTeX. It comes with a bunch of super useful default motions. Also, I figured that I should mention snippets here. Snippets aren’t done with VimTex itself, but with the LuaSnip plugin. There’s nothing super fancy going on here, but there’s one little snippet I’ll show you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_snippets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"tex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;"basetext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;[[
        \documentclass{{article}}
        \usepackage{{graphicx}}

        \begin{{document}}

        \end{{document}}
    ]]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This just makes some of the more monotonous parts of LaTeX a bit quicker. &lt;/p&gt;

&lt;p&gt;Next up, a fuzzy finder. Most Neovim users already have this installed, but I just thought I should mention it since it is a core part of my workflow. If you don’t use one, it just allows you to move between files really quickly. &lt;/p&gt;

&lt;p&gt;After that, I’d like to add a better file manager. While I haven’t configured this in Neovim yet, I use n^3 as my file browser. Neovim’s default browser is netrw, and while I do love it, it’s not super efficient. Since I’m jumping between files a lot (with and without a fuzzy finder), a better file browser is quite important to me. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqzrgcjcph8b1c4agwfn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqzrgcjcph8b1c4agwfn.png" alt="N cubed file manager" width="128" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Second to last item on this list… a spell checker! This is already built into Neovim, and it’s super easy to use and enable. I had to set it to automatically toggle off on any open terminals (because I don’t need my terminal being spell checked – fdisk definitely isn’t a word in the English dictionary).&lt;/p&gt;

&lt;p&gt;And finally, an integrated terminal. Neovim already provides a built-in terminal, but the ToggleTerm plugin allows us to toggle that on and off really quick, which is nice. My keybind for it is just “te” (my leader key is space). &lt;/p&gt;

&lt;h2&gt;
  
  
  On AI 🤖
&lt;/h2&gt;

&lt;p&gt;Oh, and by the way, I’m sure someone’s going to ask if I utilize AI at all. So to get this out of the way, no, I really don’t like AI. The one really good use case that I have found though is taking screenshots of math stuff and using ChatGPT to convert that to LaTeX. I normally write LaTeX "by hand", but there’s certain situations where I have to ask myself: “What am I really getting out of this?”. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;An example of a situation like the one mentioned...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkb4pi2n1ptuacl5oju9s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkb4pi2n1ptuacl5oju9s.png" alt="Screenshot of math stuff" width="619" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  To conclude… 🎁
&lt;/h2&gt;

&lt;p&gt;And that’s about it! With this setup, I can view Markdown and LaTeX files in Neovim, edit them with ease, and transition between filetypes with pandoc. &lt;/p&gt;

&lt;p&gt;If you’d like to see more content like this, consider following me and checking out my other socials:&lt;br&gt;
&lt;a href="https://www.youtube.com/@Oscar_CS" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/kurealnum" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>neovim</category>
      <category>vim</category>
      <category>performance</category>
    </item>
    <item>
      <title>Just hit a 1 month long streak on Github! (No automated tests/merges etc.)</title>
      <dc:creator>Oscar</dc:creator>
      <pubDate>Thu, 23 Jan 2025 17:59:18 +0000</pubDate>
      <link>https://forem.com/kurealnum/just-hit-a-1-month-long-streak-on-github-no-automated-testsmerges-etc-5e8d</link>
      <guid>https://forem.com/kurealnum/just-hit-a-1-month-long-streak-on-github-no-automated-testsmerges-etc-5e8d</guid>
      <description></description>
      <category>career</category>
      <category>github</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Daily.dev's unethical software design</title>
      <dc:creator>Oscar</dc:creator>
      <pubDate>Wed, 15 Jan 2025 15:06:51 +0000</pubDate>
      <link>https://forem.com/kurealnum/dailydevs-unethical-software-design-15ib</link>
      <guid>https://forem.com/kurealnum/dailydevs-unethical-software-design-15ib</guid>
      <description>&lt;p&gt;If you’ve watched even one Fireship video in the past 3 months, you probably know what Daily.dev is. If you don’t, it’s pretty simple. Daily.dev functions as a sort of “hub for crossposting” – a really nice idea, actually. No one is forced to use a certain platform, and writers have the potential to market to a much larger audience. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhd8ahoyk8zw1b4mo1kv4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhd8ahoyk8zw1b4mo1kv4.png" alt="Daily.dev logo" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have a few complaints about the site though. Most of them are personal though, so I won’t spend that much time on them. Here’s a short list: AI generated summaries, “meh” customization options, and lots of organizations posting slop content that I don’t really care for (this content is also super hard to get out of your feed for some reason). Those are just my opinions though.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;However&lt;/strong&gt;, there is one “complaint” I have that I think &lt;strong&gt;everyone&lt;/strong&gt; should be worried about. &lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
If you haven’t visited app.daily.dev in recent weeks, you might notice something a little bit different on your return. Below the title of each article, there’s occasionally a little yellow shield. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fviusqocs9wzisyftn5so.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fviusqocs9wzisyftn5so.png" alt="Yellow shield" width="27" height="27"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This yellow shield signifies that something on Daily.dev’s end (likely some sort of machine learning model – I’m not completely sure to be frank, as this isn’t my area of expertise) has determined that the title in question is clickbait.&lt;/p&gt;

&lt;h2&gt;
  
  
  A bad look for Daily.dev
&lt;/h2&gt;

&lt;p&gt;That in itself isn’t a great look for Daily.dev (but wait, we haven’t even gotten to the worst part!). This is absolutely something that could negatively affect an author, and thus, it’s a great way for bias to seep in. What if English is an author’s second language, and because of that, a lot of their titles sound a bit clunky to people who have spoken English their entire life? Their “clickbait” detection system might incorrectly flag that post, thus punishing an author simply because their English isn’t as good as others. &lt;/p&gt;

&lt;h3&gt;
  
  
  Some possible issues
&lt;/h3&gt;

&lt;p&gt;What if an author has a specific naming or writing style that differs from whatever this detection system prefers? If this detection system is some sort of machine learning model, what if, over time, it “learns” (again, not sure if that’s the correct term or thought process here) to flag listicles more than it does any other kind of article. I’m not the biggest fan of listicles, but there’s nothing against it in Daily.dev’s rules, thus any listicle should have the same chance of success as any other type of post. Again, this is why I think that this is something everyone should be worried about. &lt;/p&gt;

&lt;p&gt;And by the way, yes, this has already incorrectly flagged quite a few posts. Here’s an example of one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmys1rrvk5c7lkgqpj7e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmys1rrvk5c7lkgqpj7e.png" alt="Example of incorrectly flagged article" width="327" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here’s the &lt;a href="https://spin.atomicobject.com/angular-long-term-projects/?ref=dailydev" rel="noopener noreferrer"&gt;post itself&lt;/a&gt;. I think we can all agree that this clickbait detection system is completely inaccurate. &lt;/p&gt;

&lt;h2&gt;
  
  
  The worst part
&lt;/h2&gt;

&lt;p&gt;So what’s the worst part? Well, if you pay 💵8.99 USD/month, Daily.dev will &lt;em&gt;automatically&lt;/em&gt; change any title flagged as clickbait. So now we have a system that incorrectly flags content, and then automatically changes it &lt;em&gt;without&lt;/em&gt; the author’s permission. &lt;/p&gt;

&lt;p&gt;This is a wonderful example of extremely unethical behavior as well as developers that have never considered the side effects or potential bias of their software. I mean, c’mon. This is something that I learned in all 4 of the CS classes that I’ve taken – in &lt;em&gt;highschool&lt;/em&gt;. I realize that my experience isn’t universal, but considering bias and side effects should really be a fundamental skill as a software developer. &lt;/p&gt;

&lt;p&gt;On a quick side tangent: Yes, I’m sure that somewhere along the onboarding process, I probably checked a box that said “Daily.dev, you can do whatever you want with my writing”. This doesn’t make what they’re doing any less unethical though. &lt;/p&gt;

&lt;h2&gt;
  
  
  What should you do?
&lt;/h2&gt;

&lt;p&gt;This is really up to you. I likely won’t post content to Daily.dev anymore (note the disclaimer at the bottom), as I don’t want to support this behavior. I wouldn’t blame someone for staying on Daily.dev though. As I said at the beginning of this article, they allow you to market to a much wider audience, thus increasing your overall viewership, likes, followers, etc. But if biased software is what allows me to get a few more views, I refuse to be a part of it. &lt;/p&gt;

&lt;p&gt;If you have any ideas or opinions to share, please do! I'd love to hear them. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: I will post &lt;em&gt;this&lt;/em&gt; (and only this) article on Daily.dev. Maybe one of the administrators will see it and consider the issues with their clickbait detection system.  &lt;/p&gt;

&lt;p&gt;P.S: If you’d like another place to post your content that specifically doesn’t allow AI generated content, try out my content creation platform for developers, &lt;a href="https://byeai.dev" rel="noopener noreferrer"&gt;byeAI&lt;/a&gt;. Crossposting will be released soon! &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>softwareengineering</category>
      <category>ai</category>
      <category>discuss</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Oscar</dc:creator>
      <pubDate>Sat, 04 Jan 2025 13:24:58 +0000</pubDate>
      <link>https://forem.com/kurealnum/-3991</link>
      <guid>https://forem.com/kurealnum/-3991</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/ourai/does-front-end-architecture-exist-2ndm" class="crayons-story__hidden-navigation-link"&gt;Does Front-end Architecture Exist?&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/ourai" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F473938%2F8be773a3-24ff-4ca6-897c-11a48b1fe060.jpeg" alt="ourai profile" class="crayons-avatar__image" width="460" height="460"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/ourai" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Ourai L.
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Ourai L.
                
              
              &lt;div id="story-author-preview-content-2188460" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/ourai" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F473938%2F8be773a3-24ff-4ca6-897c-11a48b1fe060.jpeg" class="crayons-avatar__image" alt="" width="460" height="460"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Ourai L.&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/ourai/does-front-end-architecture-exist-2ndm" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jan 4 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/ourai/does-front-end-architecture-exist-2ndm" id="article-link-2188460"&gt;
          Does Front-end Architecture Exist?
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/softwareengineering"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;softwareengineering&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/architecture"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;architecture&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/frontend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;frontend&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/ourai/does-front-end-architecture-exist-2ndm" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/ourai/does-front-end-architecture-exist-2ndm#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              2&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            15 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>frontend</category>
      <category>architecture</category>
      <category>softwareengineering</category>
    </item>
  </channel>
</rss>
