<rss version="2.0">
  <channel>
    <title>Kristófer Reykjalín</title>
    <link>https://reykjalin.org</link>
    <description>Kristófer Reykjalín - Blog</description>
    <generator>Zine -- https://zine-ssg.io</generator>
    <language>en-US</language>
    <lastBuildDate>Mon, 29 Jun 2026 22:18:24 +0000</lastBuildDate>
    
      <item>
        <title>How to make scrollbars from scratch</title>
        <description>&lt;div class=&quot;post-title&quot;&gt;
  &lt;h1&gt;How to make scrollbars from scratch&lt;/h1&gt;
  &lt;p class=&quot;meta&quot;&gt;
    &lt;em&gt;&lt;small&gt;
        Published on
        &lt;a href=&quot;/2025/scrollbars-from-scratch/&quot;&gt;July 5, 2025&lt;/a&gt;.
      &lt;/small&gt;&lt;/em&gt;
  &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;I’ve been working on making a scroll view for
  &lt;a href=&quot;https://github.com/rockorager/libvaxis&quot; target=&quot;_blank&quot;&gt;libvaxis&lt;/a&gt;
  and calculating scrollbar dimensions in a performant way is
  &lt;strong&gt;hard&lt;/strong&gt;. Or at least doing it in a general way when you don’t necessarily know the precise dimensions of the view makes it hard.&lt;/p&gt;
&lt;p&gt;I couldn’t find any guides online for making your own scrollbar. Everything that came up was web related; CSS tricks for custom scrollbars, JavaScript implementations of scrollbars, etc. So I figured I might as well write this up and maybe someone will find this useful. And with some luck someone can tell me the actual best way to do this :)&lt;/p&gt;
&lt;h2&gt;Glossary&lt;/h2&gt;
&lt;p&gt;Let’s start by defining some terms:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Scrollbar: The whole thing you interact with.&lt;/li&gt;
  &lt;li&gt;Scroll thumb: The part of the UI that moves around to indicate the current position in a view. This fades out in most modern implementations of scrollbars.&lt;/li&gt;
  &lt;li&gt;Scroll track: The part of the UI underneath the thumb. Usually used to indicate where the scrollbar is, at least when it’s visible. Modern scrollbars don’t typically show this.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What do we need to know to make a scrollbar?&lt;/h2&gt;
&lt;p&gt;In theory, making a scrollbar is easy. You really just need to know 3 things and do some math from there:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;The length of the content you’re scrolling through.&lt;/li&gt;
  &lt;li&gt;The length of the space you have to show content.&lt;/li&gt;
  &lt;li&gt;The distance you’ve scrolled so far.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If, for whatever reason, the space you have to show content is not the same size as the space you have for the scrollbar you need some more math, but not any more information.&lt;/p&gt;
&lt;h2&gt;Calculating sizes&lt;/h2&gt;
&lt;p&gt;Once you have these 3 things you just need to do calculations. Let’s call the content length
  &lt;code&gt;l_c&lt;/code&gt;(for content length), the length of the space you have to show content
  &lt;code&gt;l_v&lt;/code&gt;
  (for view length), and the distance you’ve scrolled so far
  &lt;code&gt;d&lt;/code&gt;. What we need to calculate is then:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;The length of the scrollbar thumb,
    &lt;code&gt;l_t&lt;/code&gt;; and&lt;/li&gt;
  &lt;li&gt;the distance the thumb has moved down the scrollbar,
    &lt;code&gt;d_t&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both are proportional to the values we already know. The length of the scrollbar thumb in the scrollbar is proportional to the length of the view, and the distance the thumb has moved is proportional to the distance you’ve scrolled in the view.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;code&gt;l_t = l_v * ( l_v / l_c )&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;d_t = d * ( l_v / l_c )&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So far so good, but the math is the easy part. The hard part is actually getting the lengths needed to do the math.&lt;/p&gt;
&lt;h2&gt;The big challenge: getting the values you need for the math&lt;/h2&gt;
&lt;p&gt;The biggest challenge is getting the correct height for the content. Generally speaking you need to draw the content to know the height it will occupy on the screen. Let’s assume for a moment that the content in the scrollview is massive. Let’s say something as big as a million elements. If you want your scrollview to be performant you don’t actually want to draw all of that every time. Instead, you only want to draw what’s going to be visible in the scroll view. That way you’re not wasting time drawing something that won’t be visible.&lt;/p&gt;
&lt;p&gt;But this comes with a problem: now you don’t know the true height of the whole content, so you can’t get an accurate value for
  &lt;code&gt;l_c&lt;/code&gt;
  (content height). And without an accurate
  &lt;code&gt;l_c&lt;/code&gt;
  all of your other numbers will be wrong too, so your scrollbar will be slightly off at best, completely wrong at worst.&lt;/p&gt;
&lt;p&gt;I couldn’t find a well explained solution to this problem online. I could’ve spent days digging through open-source projects, but the ones I looked at use a completely different paradigm when it comes to rendering than
  &lt;code&gt;libvaxis&lt;/code&gt;
  does, and I didn’t feel like trying to learn a new codebase just to figure out if I could even use that approach. So I came up with a few solutions.&lt;/p&gt;
&lt;h2&gt;Solution 1: Measure the full height&lt;/h2&gt;
&lt;p&gt;This is the naïve approach, and it’s expensive. If you draw everything and measure the height you’ll have the most accurate
  &lt;code&gt;l_c&lt;/code&gt;
  possible. You’ve gone through drawing things that aren’t visible, which is absolutely a waste of time, but you can draw the scrollbar properly now.&lt;/p&gt;
&lt;p&gt;You can do things to mitigate the time you take for this. For example, you can store
  &lt;code&gt;l_c&lt;/code&gt;
  between draws and only update it when the elements in the scroll view change. This will probably cause lag when a user is changing the elements though, because every time the children change you now need to redraw every single element.&lt;/p&gt;
&lt;p&gt;Another way to approach this would be to do this calculation in a different thread so it happens in the background while other things are happening. This would remove the lag, but it adds a whole other layer of complexity that you may not want to manage.&lt;/p&gt;
&lt;p&gt;I’d consider this an appropriate approach if you’re drawing content that doesn’t change or drawing relatively few items that are inexpensive to draw. &amp;quot;Relatively few&amp;quot; here depends on many factors, such as the elements you’re drawing, the platform you’re working on, and the hardware you’re working with, just like any other performance problem.&lt;/p&gt;
&lt;h2&gt;Solution 2: Estimate full height based on what’s been drawn&lt;/h2&gt;
&lt;p&gt;Counting the child elements should, in theory, be relatively inexpensive. That depends on the implementation, of course, but typically either you as the library, or whoever is using the scroll view, will know how many elements are in the scroll view.&lt;/p&gt;
&lt;p&gt;Since we know how many elements there are in total, we can count the elements drawn in the currently visible part of the view, and use the average height of those elements to estimate how big the height of all the elements combined is.&lt;/p&gt;
&lt;p&gt;If all the elements in the scroll view have roughly the same height and are evenly distributed, this should work pretty well. But the greater the variance in height is, the worse this will look. The scroll thumb will get jumpy, and may even start jumping back and forth as you scroll in a single direction. It’s not great.&lt;/p&gt;
&lt;p&gt;I’d consider this an appropriate approach if you can be sure the elements have the same height — or something close enough to it. If that’s the case it doesn’t matter if the average height is a little off since it won’t be noticable.&lt;/p&gt;
&lt;h2&gt;Solution 3: Use proxy values to represent the heights&lt;/h2&gt;
&lt;p&gt;Instead of trying to work with the heights directly, it’s also possible to map the scroll thumb onto the scrollbar by using the indexes into the child element list to represent the heights and positions. So, instead of tracking the height of the current view, instead you track how many elements were drawn. And instead of tracking the distance you’ve scrolled down the view, you instead track how many elements you’ve scrolled past.&lt;/p&gt;
&lt;p&gt;This effectively uses the indexes you’re working with as proxies you use to map onto actual distances in the scrollbar.&lt;/p&gt;
&lt;p&gt;This does have the downside of possibly giving you an inaccurate size of the scrollbar thumb, and it can change while scrolling, if the number of elements on screen changes between draws, i.e. the height of the elements is inconsistent.&lt;/p&gt;
&lt;p&gt;You could work around the jumpiness of the scroll thumb by doing something like keeping a running average of the number of elements on the screen to try to even out the estimated height. Generally speaking I think this is a strictly worse option than Solution 2, unless there’s something specific about your situation that makes this a good proxy.&lt;/p&gt;
&lt;p&gt;In the case of
  &lt;code&gt;libvaxis&lt;/code&gt;
  the number of elements happens to be a good proxy because we’re rendering things in a terminal, so the number of elements maps pretty well onto rows of text rendered there.&lt;/p&gt;
&lt;h2&gt;Solution 4: Cheat&lt;/h2&gt;
&lt;p&gt;Well, not literally.&lt;/p&gt;
&lt;p&gt;The way I ended up doing this was to use Solution 3 and estimate the height of the content based on what’s being rendered, using the number of elements drawn and the total number of elements as proxies for the content height.&lt;/p&gt;
&lt;p&gt;The cheating that we do is to ask whoever is using the library to provide an explicit height for all of the content. Since whoever is using the library will probably know the height of the content anyway, they might as well include it!&lt;/p&gt;
&lt;p&gt;So you get the best of both worlds. If you don’t want to bother calculating the total height yourself — or can’t calculate it for some reason — the estimate is good enough for most cases, and pretty performant. However, if you do already know the total height we can skip the measurements and go straight to the math, which is very performant!&lt;/p&gt;
&lt;p&gt;It’s not a perfect API, and far from seamless. But it works pretty well, and I’m quite happy with the solution as it is.&lt;/p&gt;
</description>
        <link>https://reykjalin.org/blog/scrollbars-from-scratch/</link>
        <pubDate>Sat, 05 Jul 2025 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/scrollbars-from-scratch/</guid>
      </item>
    
      <item>
        <title>Interfaces are too vertical</title>
        <description>&lt;div class=&quot;post-title&quot;&gt;
  &lt;h1&gt;Interfaces are too vertical&lt;/h1&gt;
  &lt;p class=&quot;meta&quot;&gt;
    &lt;em&gt;&lt;small&gt;
        Published on
        &lt;a href=&quot;/2024/10/09/interfaces-are-too-vertical&quot;&gt;October 9, 2024&lt;/a&gt;.
      &lt;/small&gt;&lt;/em&gt;
  &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;
  I find that many websites today — interfaces in general, really — are laid out vertically.
  Most of the time this is fine, but when it’s on pages focused on content, such as wikis, blogs, and even news websites, I’ve started wondering why?
&lt;/p&gt;
&lt;p&gt;
  It makes some sense of course, we generally read from top to bottom after all.
  But if the most important bit of the page is the content, why is a good quarter of the screen taken up by a logo, navigation, and potentially other elements that don’t add any value to the content?
&lt;/p&gt;
&lt;p&gt;
  I get this for mobile layouts or other screen sizes where you don’t have a lot of horizontal space.
  I still think taking up half the screen for a logo and navigation isn’t the best approach, but it’s understandable at least.
  On wider screens, basically anything other than a phone, you have more space to work with along the horizontal axis.
  And I think we should use that space! Bring those sidebars back!
&lt;/p&gt;
&lt;p&gt;
  This occurred to me as I was listening to an episode of
  &lt;a href=&quot;https://open.spotify.com/show/512srmQyB2LQTLVQzIsFV3&quot; target=&quot;_blank&quot;&gt;the Arc Browser podcast&lt;/a&gt;.
  They’re working on the next major version of Arc and want to get to the core value statement of Arc.
  One of those, to me, is the sidebar layout.
  Having more space to read the pages I’m browsing improves the feel of the browser significantly.
  And stacking tabs vertically makes more sense to me than laying them out horizontally.
&lt;/p&gt;
&lt;p&gt;
  We consume content too-to-bottom, so why have the browser UI get in the way of that by putting it all above the web page you’re browsing?
  That’s getting in the way of you consuming the content on the page.
&lt;/p&gt;
&lt;p&gt;
  This doesn’t apply so much where the first thing someone might interact with is the navigation, but on something like a personal blog it seems to me that a sidebar makes sense.
  Where the navigation isn’t getting in the way of the content.
&lt;/p&gt;
&lt;h2&gt;New site design&lt;/h2&gt;
&lt;p&gt;
  Having had all these thoughts I decided to make a new theme for the site! Again! The navigation is now on a sidebar if the page is wide enough.
  If not, the navigation appears underneath the content.
  There’s  also a link at the top of the page to skip over the content straight to the navigation when needed.
&lt;/p&gt;
&lt;p&gt;
  This makes more sense to me personally.
  I never pay any attention to the header or navigation on pages I visit.
  I’m much more interested in the actual content.
&lt;/p&gt;
&lt;p&gt;
  I also made some strong stylistic choices with this theme.
  I’m only using named CSS colors, it only has a dark mode, and using
  &lt;code&gt;magenta&lt;/code&gt;
  as the accent color is certainly a bit of a statement!
&lt;/p&gt;
&lt;p&gt;I hope you like it!&lt;/p&gt;
</description>
        <link>https://reykjalin.org/blog/interfaces-too-vertical/</link>
        <pubDate>Wed, 09 Oct 2024 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/interfaces-too-vertical/</guid>
      </item>
    
      <item>
        <title>This site is now hand-crafted</title>
        <description>&lt;p&gt;You’re now looking at a complete redesign of this site, but it’s actually more than that. This site is now entirely hand-crafted in HTML. Or well, it’s actually PHP, but that’s just for the reusable parts, like the header and footer, but the posts are all handwritten HTML. Wild right? Why would I do that? Why would anyone do that?&lt;/p&gt;&lt;p&gt;I’ve been using &lt;a href=&quot;https://wordpress.org&quot; target=&quot;_blank&quot;&gt;WordPress&lt;/a&gt; to host my blog for years now, but it’s always felt a bit restrictive to do things the WordPress way. This way I have complete control over what my blog looks like and what the individual pages do.&lt;/p&gt;&lt;p&gt;I’ve also decided to not just redesign the look of the site, but also the content. Or rather, I plan to write about a wider range of things that interest me.&lt;/p&gt;&lt;p&gt;If you’re interested in learning more about the current design you can read &lt;a href=&quot;https://reykjalin.org/blog/2024-style-refresh/&quot;&gt;my post about this design iteration&lt;/a&gt;. This post is more about the reason behind this redesign than the redesign itself.&lt;/p&gt;&lt;h2&gt;Control&lt;/h2&gt;&lt;p&gt;Let’s start by talking about the increased control this approach offers. By writing the site content entirely by hand I have full control over every individual page on my site. I don’t need to create custom templates or post types to get different looking pages. I just write the markup and there it is!&lt;/p&gt;&lt;p&gt;This also gives me room to experiment with other things, e.g. make hyper custom one-off JS components for just a single post or page. One example of this is the YNAB I’m working on which would be a pain to host on a WordPress site.&lt;/p&gt;&lt;p&gt;I can experiment with new web technologies and write a page in Svelte or React. I could play around with WebAssembly or in-browser database engines.&lt;/p&gt;&lt;p&gt;Writing the site by hand opens up a world of possibilities when it comes to what I can do on the site.&lt;/p&gt;&lt;h2&gt;Hosting&lt;/h2&gt;&lt;p&gt;Another aspect of improved control is hosting. Since the site is all simple PHP I can host it pretty much anywhere. I’m not bound to hosts that can deal with WordPress or some other CMS.&lt;/p&gt;&lt;p&gt;Sure, the host will need to support PHP, but honestly which website host doesn’t? That’s barely even a limitation.&lt;/p&gt;&lt;p&gt;I’ve decided to try the hosting offered by &lt;a href=&quot;https://www.nearlyfreespeech.net&quot; target=&quot;_blank&quot;&gt;NearlyFreeSpeech.NET&lt;/a&gt;. Their DIY, bare-bones offering really appeals to my inner minimalist. And with the basic tech stack necessary to run this version of the blog it should cost me less than US$5 &lt;em&gt;per year&lt;/em&gt;. That’s a really nice price point that’s tough to beat.&lt;/p&gt;&lt;p&gt;The downside is that they don’t do much for you, you really do get what you pay for. However, that works really well for me since that’s what I prefer anyway, but might not be for everyone.&lt;/p&gt;&lt;div id=&quot;history&quot; class=&quot;block&quot;&gt;&lt;h2&gt;History&lt;/h2&gt;&lt;/div&gt;&lt;p&gt;Or, to phrase the title of this section more completely; the history of my blog. I redesign my blog &lt;em&gt;often&lt;/em&gt;. I’m a little sad the history of those changes doesn’t really exist. I’ve tried to archive my posts after I make them in &lt;a href=&quot;https://web.archive.org&quot; target=&quot;_blank&quot;&gt;the WayBack Machine&lt;/a&gt;, but that doesn’t capture the redesigns when I don’t make new blog posts in between the redesigns, since I only archive posts once when they’re published.&lt;/p&gt;&lt;p&gt;So, I’ve decided that going forward, after a post is published, it will exist in that state forever. I will not be retroactively going back to update the styles in a post. Even the header and footer will remain the same. A redesign will start a new stylesheet and set of reusable functions, so old posts will remain unchanged. This way you can go back in time, not just in terms of content, i.e. older posts, but also in terms of site design!&lt;/p&gt;&lt;p&gt;I’m &lt;em&gt;very excited&lt;/em&gt; to see how this will turn out.&lt;/p&gt;&lt;h2&gt;More varied content&lt;/h2&gt;&lt;p&gt;Up until now I’ve kept my blog fairly technical. I &lt;a href=&quot;https://reykjalin.org/about/&quot;&gt;work in tech&lt;/a&gt; and it’s a big part of my life. I love technology and I love programming. I used to look at my blog as a way to express that.&lt;/p&gt;&lt;p&gt;But I found it hard to muster up the enthusiasm to actually write for the blog. A big part of that is because I live and breathe tech all day, even outside work when I’m working on my hobbies. I don’t really need another outlet for tech.&lt;/p&gt;&lt;p&gt;I have other hobbies and interests. One of those is Pokémon. I play the video games and TCG, and there’s plenty there to write about, even if only to show off my favorite cards out of the (many, &lt;em&gt;many&lt;/em&gt;) TCG packs I open.&lt;/p&gt;&lt;p&gt;I also play other video games that I can write about. I read a decent amount of books and I’m a huge fan of several book series both ongoing and completed. That’s something else I could write about.&lt;/p&gt;&lt;p&gt;I recently became a father to a beautiful girl and that’s something else I &lt;em&gt;might&lt;/em&gt; go into at some point, but I’m still a bit unsure of whether I’m willing to go into something that personal here.&lt;/p&gt;&lt;p&gt;I’m feeling excited about pivoting the blog like this and I’m already looking forward to sharing photos of the Pokémon cards I got from opening some tins and packs from the latest set, Paldean Fates. I finally feel excited to publish something here again.&lt;/p&gt;&lt;p&gt;Now, that doesn’t mean I won’t be writing about tech at all. I still plan on doing that, just to a lesser extent. Or maybe not “to a lesser extent”, I guess I didn’t write much to begin with, huh?&lt;/p&gt;&lt;p&gt;Anyway, I just don’t want to &lt;em&gt;limit myself&lt;/em&gt; to tech anymore. Maybe that will breathe some life into the blog, yeah? At least my enthusiasm for it seems to be returning, that seems like a good sign.&lt;/p&gt;&lt;p&gt;All of that to say: I want this blog to be more than just a tech blog. To open myself up to posting more varied types of content and see if that won’t result in me actually &lt;em&gt;blogging&lt;/em&gt; instead of just redesigning the site twice a year.&lt;/p&gt;&lt;h2&gt;Rough edges&lt;/h2&gt;&lt;p&gt;As i mentioned, the site is all hand-crafted HTML, and I’ll admit: it’s a bit of a pain. The writing experience is certainly different. But I don’t post frequently to begin with and don’t plan to go on some posting spree now. A post every month or two is the cadence I’m going for.&lt;/p&gt;&lt;p&gt;Maintaining the RSS feed is also a bit tedious — yes, the RSS feed is also handwritten — but it’s not so bad. Mostly just copy-pasting the HTML from the posts to the feed file and making some minor style adjustments here and there.&lt;/p&gt;&lt;p&gt;And that’s really it. The writing experience is different, but this setup comes with so much freedom and control that it offsets the minor annoyance of doing things by hand. That’s also a solvable problem! I’m writing this in &lt;a href=&quot;https://papereditor.app&quot; target=&quot;_blank&quot;&gt;Paper&lt;/a&gt;, a Markdown editor with a wonderful writing experience, generous pricing model, and exports to HTML.&lt;/p&gt;&lt;p&gt;Once I’m done writing the post I can even use &lt;a href=&quot;https://workingcopyapp.com&quot; target=&quot;_blank&quot;&gt;WorkingCopy&lt;/a&gt; to push the post from my phone to the git repo and clean it up later before publishing from my computer.&lt;/p&gt;&lt;p&gt;So far I’m very happy with this setup. The migration itself was a &lt;em&gt;huge chore&lt;/em&gt;, but now that it’s done I think the whole setup is quite nice.&lt;/p&gt;&lt;p&gt;I’m very excited to see how this turns out once I have some more posts up and eventually redesign the site again. That’s going to be the real test for the setup.&lt;/p&gt;</description>
        <link>https://reykjalin.org/blog/hand-crafted/</link>
        <pubDate>Sun, 10 Mar 2024 00:00:01 +0000</pubDate>
        <guid>https://reykjalin.org/blog/hand-crafted/</guid>
      </item>
    
      <item>
        <title>March 2024 style refresh</title>
        <description>&lt;p&gt;While &lt;a href=&quot;https://reykjalin.org/blog/hand-crafted/&quot;&gt;redesigning my website&lt;/a&gt; I needed a way to get a proper look at what the various HTML elements look like as I was making the changes, so I’m writing this post to do just that. I also want to mention some of the choices made in the design of the website.&lt;/p&gt;&lt;p&gt;The website only has a dark mode. I find that it’s much easier on my eyes, and easier to read any time of day. Especially so at night when I want to look something up real quick.&lt;/p&gt;&lt;p&gt;I decided to use the &lt;a href=&quot;https://brailleinstitute.org/freefont&quot; target=&quot;_blank&quot;&gt;Atkinson Hyperlegible&lt;/a&gt; font for the body text on the site this time around. I like the way it looks and it is, at least supposedly, designed to be easier to read than other fonts. I find it nice to read myself and quite like the font. The headings use Charter, and the code examples use &lt;a href=&quot;https://www.jetbrains.com/lp/mono/&quot; target=&quot;_blank&quot;&gt;JetBrains Mono&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I’m quite happy with this mix of fonts, I think they go really well together.&lt;/p&gt;&lt;p&gt;The colors in use are all based on the &lt;a href=&quot;https://yeun.github.io/open-color/&quot; target=&quot;_blank&quot;&gt;Open Color&lt;/a&gt; palette. I didn’t want to come up with all the colors myself, so I decided to pick colors from there. I think this turned out quite nice.&lt;/p&gt;&lt;p&gt;There are some color combinations I’m not quite happy with, e.g. the &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; elements, but they’re not terrible. I’m also not quite happy with the colors for links. I’ll probably tweak these a bit in the future.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://reykjalin.org/2024-03.css&quot;&gt;The stylesheet&lt;/a&gt; sets variables to control the various colors and spacings on the site, mostly intended to make it easy for me to tweak the design later. I don’t think this will be the final iteration of this particular design, but it looks good enough right now.&lt;/p&gt;&lt;p&gt;Having read a recent post from Bradley Taunt on &lt;a href=&quot;https://btxx.org/posts/Please_Make_Your_Table_Headings_Sticky/&quot; target=&quot;_blank&quot;&gt;sticky table headings&lt;/a&gt; I’ve gone ahead and made the table headers for the &lt;a href=&quot;https://reykjalin.org/salary/&quot;&gt;one table on this site&lt;/a&gt; sticky.&lt;/p&gt;&lt;p&gt;And finally I decided to append a little icon to external links. I chose to go with the &lt;a href=&quot;https://en.wikipedia.org/wiki/Esc_key&quot; target=&quot;_blank&quot;&gt;ISO symbol for “Escape”&lt;/a&gt;, which looks like this: ⎋. You’ll see this icon appended to any external links on the site.&lt;/p&gt;&lt;h2&gt;A look at the various HTML elements&lt;/h2&gt;&lt;h2&gt;This is an &lt;code&gt;h1&lt;/code&gt; tag&lt;/h2&gt;&lt;h2&gt;This is an &lt;code&gt;h2&lt;/code&gt; tag&lt;/h2&gt;&lt;h3&gt;This is an &lt;code&gt;h3&lt;/code&gt; tag&lt;/h3&gt;&lt;h4&gt;This is an &lt;code&gt;h4&lt;/code&gt; tag&lt;/h4&gt;&lt;h5&gt;This is an &lt;code&gt;h5&lt;/code&gt; tag&lt;/h5&gt;&lt;h6&gt;This is an &lt;code&gt;h6&lt;/code&gt; tag&lt;/h6&gt;&lt;p&gt;This is a paragraph.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://reykjalin.org/&quot;&gt;This is a link&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://wikipedia.org&quot; target=&quot;_blank&quot;&gt;This is an external link&lt;/a&gt; to Wikipedia.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;This is an item in an unordered list.&lt;ul&gt;&lt;li&gt;This is a nested item in a nested list.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;ol&gt;&lt;li&gt;This is an item in an ordered list.&lt;ol&gt;&lt;li&gt;This is a nested item in a nested list.&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;This is a quote.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;code&gt;This is a code tag.&lt;/code&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;This is a pre tag.
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code class=&quot;cpp&quot;&gt;&lt;span class=&quot;comment&quot;&gt;// This has C++ code.&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;main&lt;/span&gt;() {
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;comment&quot;&gt;// This has JavaScript code.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;block info notice&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;block title&quot;&gt;&lt;p&gt; Info&lt;/p&gt;&lt;/div&gt;&lt;p&gt;This is an info notice.&lt;/p&gt;&lt;/div&gt;&lt;div class=&quot;block warning notice&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;block title&quot;&gt;&lt;p&gt; Warning!&lt;/p&gt;&lt;/div&gt;&lt;p&gt;This is a warning notice.&lt;/p&gt;&lt;/div&gt;</description>
        <link>https://reykjalin.org/blog/2024-style-refresh/</link>
        <pubDate>Sun, 10 Mar 2024 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/2024-style-refresh/</guid>
      </item>
    
      <item>
        <title>Paged.js for creating printable documents with HTML and CSS</title>
        <description>&lt;p&gt;
				I just came across &lt;a href=&quot;https://www.pagedjs.org/&quot;&gt;paged.js&lt;/a&gt;, a JavaScript library
				specifically made to make it easy to use HTML and CSS to make websites that look nice when
				printed or saved as PDFs, and it looks really interesting.
				&lt;a href=&quot;https://pdf.math.dev&quot;&gt;The demo&lt;/a&gt; that lead to my discovery of the library looks
				especially appealing.
			&lt;/p&gt;

			&lt;p&gt;
				I think this might finally lead to me &lt;em&gt;wanting&lt;/em&gt; to maintain my CV as a HTML document.
				Even writing other types of documents using paged.js sounds appealing. At a glance this
				seems much better, in my opinion, than using a typesetting system like
				&lt;a href=&quot;https://www.latex-project.org&quot;&gt;LaTeX&lt;/a&gt; or an office suite like
				&lt;a href=&quot;https://www.libreoffice.org&quot;&gt;LibreOffice&lt;/a&gt;.
			&lt;/p&gt;

			&lt;p&gt;I’m really looking forward to taking this for a spin!&lt;/p&gt;
</description>
        <link>https://reykjalin.org/blog/paged-js/</link>
        <pubDate>Mon, 05 Apr 2021 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/paged-js/</guid>
      </item>
    
      <item>
        <title>git send-email not working on macOS?</title>
        <description>&lt;p&gt;
				I just spent 2 hours trying to send a patch to a mailing list via
				&lt;code&gt;git send-email&lt;/code&gt; on my MacBook Pro, and kept getting this error:
			&lt;/p&gt;

			&lt;pre class=&quot;language-shell&quot;&gt;&lt;code&gt;$ git send-email --to=&quot;&amp;lt;mailing_list&amp;gt;&quot; HEAD^
Unable to initialize SMTP properly. Check config and use --smtp-debug. VALUES: server=smtp.migadu.com encryption=tls hello=localhost.localdomain port=465 at /usr/local/Cellar/git/2.30.2/libexec/git-core/git-send-email line 1566, &amp;lt;FIN&amp;gt; line 1.&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;
				If you include &lt;code&gt;--smtp-debug=1&lt;/code&gt; in your command, you might see something like
				this:
			&lt;/p&gt;

			&lt;pre
			class=&quot;language-shell&quot;
			&gt;&lt;code&gt;$ git send-email --to=&quot;&amp;lt;mailing_list&amp;gt;&quot; HEAD^ --smtp-debug=1
Net::SMTP&gt;&gt;&gt; Net::SMTP(3.11)
Net::SMTP&gt;&gt;&gt;   Net::Cmd(3.11)
Net::SMTP&gt;&gt;&gt;     Exporter(5.73)
Net::SMTP&gt;&gt;&gt;   IO::Socket::IP(0.39)
Net::SMTP&gt;&gt;&gt;     IO::Socket(1.39)
Net::SMTP&gt;&gt;&gt;       IO::Handle(1.39)
Net::SMTP: Net::Cmd::getline(): unexpected EOF on command channel:  at /usr/local/Cellar/git/2.30.2/libexec/git-core/git-send-email line 1539.
Unable to initialize SMTP properly. Check config and use --smtp-debug. VALUES: server=smtp.migadu.com encryption=tls hello=localhost.localdomain port=465 at /usr/local/Cellar/git/2.30.2/libexec/git-core/git-send-email line 1566, &amp;lt;FIN&amp;gt; line 1.&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;
				After spending 2 hours trying to find a fix for this online, the solution turns out to be
				simple: set &lt;code&gt;smtpEncryption&lt;/code&gt; to &lt;code&gt;ssl&lt;/code&gt; in your git config.
			&lt;/p&gt;

			&lt;p&gt;
				See, I recently moved my email provider from
				&lt;a href=&quot;https://mailbox.org/en/&quot;&gt;Mailbox.org&lt;/a&gt; to
				&lt;a href=&quot;https://www.migadu.com&quot;&gt;Migadu&lt;/a&gt;, and as per
				&lt;a href=&quot;https://www.migadu.com/guides/index.html#generic-settings&quot;&gt;Migadu documentation&lt;/a&gt;
				I set &lt;code&gt;smtpEncryption&lt;/code&gt; to &lt;code&gt;tls&lt;/code&gt;. No amount of tweaking configs,
				&lt;code&gt;git send-email&lt;/code&gt; parameters, or updating perl packages seemed to fix the issue.
			&lt;/p&gt;

			&lt;p&gt;
				Finally, when I notice that my previous config used &lt;code&gt;ssl&lt;/code&gt; instead of
				&lt;code&gt;tls&lt;/code&gt; I tried to change the value and that worked!
			&lt;/p&gt;

			&lt;p&gt;
				So, if whoever lands here is having the same issue, just try setting
				&lt;code&gt;smtpEncryption&lt;/code&gt; to &lt;code&gt;ssl&lt;/code&gt; instead of &lt;code&gt;tls&lt;/code&gt; and see if that
				works. Even if your email provider says to use &lt;code&gt;tls&lt;/code&gt;.
			&lt;/p&gt;

			&lt;p&gt;Example configuration:&lt;/p&gt;

			&lt;pre class=&quot;language-ini&quot;&gt;&lt;code&gt;[sendemail]
	smtpServer = smtp.migadu.com
	smtpUser = &amp;lt;smtp_username&amp;gt;
	smtpEncryption = ssl
	smtpServerPort = 465&lt;/code&gt;&lt;/pre&gt;
</description>
        <link>https://reykjalin.org/blog/git-send-email-macos/</link>
        <pubDate>Fri, 26 Mar 2021 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/git-send-email-macos/</guid>
      </item>
    
      <item>
        <title>Managing your site with WordOps</title>
        <description>&lt;p&gt;
				&lt;a href=&quot;https://wordops.net&quot;&gt;WordOps&lt;/a&gt; is a wonderful tool, specifically made to make
				hosting WordPress websites easier, although they also provide excellent support for setting
				up and managing static sites, reverse proxies, and the like. It’s a wonderful tool for
				managing websites, and I highly recommend you use it.
			&lt;/p&gt;

			&lt;p&gt;
				I want to to take you through the steps necessary to install and configure WordOps, and
				launching your first WordPress site!
			&lt;/p&gt;

			&lt;h2 id=&quot;before-you-get-started&quot;&gt;Before you get started&lt;/h2&gt;

			&lt;p&gt;Make sure you have the following available before you start:&lt;/p&gt;

			&lt;ol&gt;
				&lt;li&gt;A server.&lt;/li&gt;
				&lt;li&gt;
					A domain that points to the server; I&apos;ll be using &lt;code&gt;thorlaksson.com&lt;/code&gt; as the
					example domain in this post.
				&lt;/li&gt;
			&lt;/ol&gt;

			&lt;p&gt;
				I’ll be using an Ubuntu 20.04 server with an expandable volume attached where I will host
				sites, &lt;a href=&quot;#mounting-the-volume&quot;&gt;which will require some additional setup&lt;/a&gt;.
			&lt;/p&gt;

			&lt;div class=&quot;info notice&quot;&gt;
				&lt;p class=&quot;title&quot;&gt;Tip!&lt;/p&gt;
				&lt;p&gt;WordOps also supports Debian and Raspbian, if you prefer either of those over Ubuntu.&lt;/p&gt;
			&lt;/div&gt;

			&lt;h2 id=&quot;mounting-the-volume&quot;&gt;Mounting the volume for expandable storage&lt;/h2&gt;

			&lt;div class=&quot;info notice&quot;&gt;
				&lt;p class=&quot;title&quot;&gt;Note!&lt;/p&gt;
				&lt;p&gt;
					You can skip this section entirely if you don’t need to mount a volume and jump straight
					to &lt;a href=&quot;#installing-wordops&quot;&gt;the WordOps installation&lt;/a&gt;.
				&lt;/p&gt;
			&lt;/div&gt;

			&lt;p&gt;
				I’m using &lt;a href=&quot;https://www.hetzner.com&quot;&gt;Hetzner&lt;/a&gt; to host the server, so these
				instructions will be specific to servers hosted with them. The process should be similar for
				any other service, but there might be a few differences.
			&lt;/p&gt;

			&lt;p&gt;
				First, delete your &lt;code&gt;/var/www/&lt;/code&gt; directory. If you already have things there you
				want to keep, make sure you back them up.
			&lt;/p&gt;

			&lt;pre class=&quot;language-bash&quot;&gt;&lt;code&gt;cp -r /var/www ~/ # For backup purposes
sudo rm -rf /var/www&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;
				Open your &lt;code&gt;fstab&lt;/code&gt; file with your favorite editor and mount your volume there.
			&lt;/p&gt;

			&lt;pre class=&quot;language-bash&quot;&gt;&lt;code&gt;sudo nano /etc/fstab # to open the file with nano
sudo vi /etc/fstab # to open the file with vi&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;
				Add the following line to your &lt;code&gt;fstab&lt;/code&gt;, and make sure to change the volume ID.
			&lt;/p&gt;

			&lt;pre class=&quot;language-plaintext&quot;&gt;&lt;code&gt;/dev/disk/by-id/&amp;lt;volume_id&amp;gt; /var/www ext4 discard,nofail,defaults 0 0&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;Your &lt;code&gt;fstab&lt;/code&gt; should now look something like this.&lt;/p&gt;

			&lt;pre class=&quot;language-plaintext&quot;&gt;&lt;code&gt;# /etc/fstab: static file system information.
UUID=2B0E-1DC7  /boot/efi       vfat    umask=0077      0       1
/dev/disk/by-id/scsi-0HC_Volume_10143990 /var/www ext4 discard,nofail,defaults 0 0&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;Now, reboot your server, and ssh back into it.&lt;/p&gt;

			&lt;pre class=&quot;language-bash&quot;&gt;&lt;code&gt;sudo reboot&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;
				Once your server is up again, make sure the &lt;code&gt;/var/www/&lt;/code&gt; directory has the correct
				ownership setup.
			&lt;/p&gt;

			&lt;pre class=&quot;language-bash&quot;&gt;&lt;code&gt;sudo chown -R www-data:www-data /var/www&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;And now we’re ready to setup WordOps!&lt;/p&gt;

			&lt;h2 id=&quot;installing-wordops&quot;&gt;Installing WordOps&lt;/h2&gt;

			&lt;p&gt;To install WordOps use ssh to connect to your server and run the installation command.&lt;/p&gt;

			&lt;pre class=&quot;language-bash&quot;&gt;&lt;code&gt;wget -qO wo wops.cc &amp;&amp; sudo bash wo&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;This will install WordOps and its dependencies on your system.&lt;/p&gt;

			&lt;div class=&quot;info notice&quot;&gt;
				&lt;p class=&quot;title&quot;&gt;Tip!&lt;/p&gt;
				&lt;p&gt;
					Many people are uncomfortable with piping scripts to &lt;code&gt;sudo&lt;/code&gt; for a good reason.
					If you prefer you can clone the repository from git and install it that way:
				&lt;/p&gt;
				&lt;pre class=&quot;language-bash&quot;&gt;&lt;code&gt;git clone https://github.com/WordOps/WordOps.git
cd WordOps/
sudo bash install&lt;/code&gt;&lt;/pre&gt;
				&lt;p&gt;
					WordOps also provides a set of fully
					&lt;a href=&quot;https://docs.wordops.net/getting-started/installation-guide/#manual-installation&quot;&gt;manual installation&lt;/a&gt;
					steps if you prefer to &lt;em&gt;really&lt;/em&gt; do everything yourself.
				&lt;/p&gt;
			&lt;/div&gt;

			&lt;p&gt;
				WordOps requires super user privileges to manage everything for you, so it’s a good idea to
				add a command alias to automatically run the &lt;code&gt;wo&lt;/code&gt; command with super user
				privileges.
			&lt;/p&gt;

			&lt;pre class=&quot;language-bash&quot;&gt;&lt;code&gt;echo -e &quot;alias wo=&apos;sudo -E wo&apos;&quot; &gt;&gt; $HOME/.bashrc&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;
				Now that the &lt;code&gt;wo&lt;/code&gt; tool has been installed, we use WordOps to install the
				technology stack used to run the websites. You can see precisely what will be installed
				&lt;a href=&quot;https://docs.wordops.net/getting-started/post-install-steps/#installing-wordops-stacks-optional&quot;&gt;here&lt;/a&gt;.
			&lt;/p&gt;

			&lt;pre class=&quot;language-bash&quot;&gt;&lt;code&gt;wo stack install&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;
				During this installation WordOps will create credentials for the administrative dashboard,
				but we’ll change those with the next command.
			&lt;/p&gt;

			&lt;pre class=&quot;language-bash&quot;&gt;&lt;code&gt;wo secure --auth&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;
				You’ll be prompted for a username and password. Generate a strong password with you password
				manager and save it there.
			&lt;/p&gt;

			&lt;p&gt;Remember to enable the firewall too!&lt;/p&gt;

			&lt;pre class=&quot;language-bash&quot;&gt;&lt;code&gt;wo stack install --ufw&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;
				Now, you should be able to look at the admin dashboard at
				&lt;code&gt;https://&amp;lt;domain&amp;gt;:22222&lt;/code&gt;. In my case, that’s
				&lt;code&gt;https://thorlaksson.com:22222&lt;/code&gt;.
			&lt;/p&gt;

			&lt;p&gt;
				The SSL certificate won’t be valid for the admin dashboard, but don’t worry about that; that
				will be fixed when we create our first site.
			&lt;/p&gt;

			&lt;h2 id=&quot;creating-your-first-wordpress-site&quot;&gt;Creating your first WordPress site&lt;/h2&gt;

			&lt;p&gt;Now it’s time to setup a WordPress site! 🎉&lt;/p&gt;

			&lt;p&gt;Run the &lt;code&gt;site create&lt;/code&gt; command with a couple extra flags.&lt;/p&gt;

			&lt;pre class=&quot;language-bash&quot;&gt;&lt;code&gt;wo site create thorlaksson.com --wp --php74 --user=&amp;lt;wp_admin_username&amp;gt; --email=&amp;lt;admin_email&amp;gt; -le&lt;/code&gt;&lt;/pre&gt;

			&lt;ul&gt;
				&lt;li&gt;&lt;code&gt;--wp&lt;/code&gt; to setup a WordPress site.&lt;/li&gt;
				&lt;li&gt;&lt;code&gt;--php74&lt;/code&gt; to use PHP 7.4.&lt;/li&gt;
				&lt;li&gt;&lt;code&gt;--user=&amp;lt;wp_admin_username&amp;gt;&lt;/code&gt; to choose the admin username we want.&lt;/li&gt;
				&lt;li&gt;
					&lt;code&gt;--email=&amp;lt;admin_email&amp;gt;&lt;/code&gt; to set the email for the WordPress admin user.
				&lt;/li&gt;
				&lt;li&gt;&lt;code&gt;-le&lt;/code&gt; to enable Let’s Encrypt and SSL.&lt;/li&gt;
			&lt;/ul&gt;

			&lt;p&gt;
				Once that command has wrapped up you should have a WordPress site ready to go, with the
				username and password to login printed out during the installation.
			&lt;/p&gt;

			&lt;div class=&quot;info notice&quot;&gt;
				&lt;p class=&quot;title&quot;&gt;Tip!&lt;/p&gt;
				&lt;p&gt;
					I created a simple WordPress site here with no caching set up. You can change that by
					replacing the &lt;code&gt;--wp&lt;/code&gt; flag with any of the other
					&lt;a href=&quot;https://docs.wordops.net/commands/site/#cheatsheet&quot;&gt;WordPress flags&lt;/a&gt; to set up
					caching with WP Rocket, Redis, or any of the other caching solutions supported.
				&lt;/p&gt;
			&lt;/div&gt;

			&lt;p&gt;
				I recommend you change the admin password immediately when you first login to the WordPress
				site. Use your password manager to generate something stronger than the password generated
				by default.
			&lt;/p&gt;

			&lt;p&gt;
				Before we login though, let’s install some essential plugins, with WP CLI — a tool WordOps
				installed for us! Just make sure to run these commands with &lt;code&gt;sudo&lt;/code&gt; or as
				&lt;code&gt;www-data&lt;/code&gt;.
			&lt;/p&gt;

			&lt;pre class=&quot;language-bash&quot;&gt;&lt;code&gt;wo site cd thorlaksson.com
cd htdocs
# You should now be in /var/www/&amp;lt;domain&amp;gt;/htdocs

# Now let&apos;s install some plugins
wp plugin install akismet --allow-root --activate # To prevent comment spam
wp plugin install gutenberg --allow-root --activate # To use the latest version of Gutenberg
wp plugin install two-factor --allow-root --activate # So we can use 2fa for website accounts
wp plugin install wordpress-seo --allow-root --activate # For SEO

# Fix permissions
sudo chown www-data:www-data wp-content -R&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;
				You can use commands like this to
				&lt;a href=&quot;https://docs.wordops.net/how-to/automate-wordops-install/&quot;&gt;automate the setup of new sites&lt;/a&gt;.
				If you manage multiple sites, or setup new sites on a regular basis, having a script to
				set everything up for you is an amazing boon.
			&lt;/p&gt;

			&lt;p&gt;
				Of course, you can remove or add plugins here as you like, or simply do this all through the
				WordPress dashboard.
			&lt;/p&gt;

			&lt;p&gt;
				Finally, let’s make sure everything on the server is up to date. You can run this same
				command whenever you want to install updates on your server.
			&lt;/p&gt;

			&lt;pre class=&quot;language-bash&quot;&gt;&lt;code&gt;wo maintenance&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;And that’s it! Your WordPress site is now ready at &lt;code&gt;https://&amp;lt;domain&amp;gt;&lt;/code&gt;!&lt;/p&gt;

			&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

			&lt;p&gt;
				WordOps is an extremely versatile tool for website hosting, especially if you’re managing a
				WordPress site, or even multiple WordPress sites. I personally use this to great effect to
				set up testing sites at work.
			&lt;/p&gt;

			&lt;p&gt;
				WordOps is also useful for just managing your web server’s reverse proxies or static sites.
				I didn’t talk about this much here, but refer to
				&lt;a href=&quot;https://docs.wordops.net/commands/site/#basic-sites&quot;&gt;the documentation&lt;/a&gt; for how
				to set those up.
			&lt;/p&gt;

			&lt;p&gt;
				Being able to spin up a site quickly without having to worry about managing a web server,
				keeping dependencies up to date, setting up a database, and securing everything saves so
				much time.
			&lt;/p&gt;

			&lt;p&gt;
				Finally, you can just focus on managing a &lt;em&gt;website&lt;/em&gt; instead of managing a
				&lt;em&gt;server&lt;/em&gt;.
			&lt;/p&gt;
</description>
        <link>https://reykjalin.org/blog/wordops/</link>
        <pubDate>Sat, 20 Mar 2021 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/wordops/</guid>
      </item>
    
      <item>
        <title>&quot;On the experience of being poor-ish, for people who aren&apos;t&quot;</title>
        <description>&lt;p&gt;
				&lt;a href=&quot;https://residentcontrarian.substack.com/p/on-the-experience-of-being-poor-ish&quot;&gt;
					&quot;On the experience of being poor-ish, for people who aren&apos;t&quot;
				&lt;/a&gt;
				is a fantastic article about the experience of someone figuring out how to survive when
				you’re borderline broke. Broke in the literal sense. The &quot;I had to choose between feeding my
				family or paying for electricity so I could work from home&quot; kind.
			&lt;/p&gt;

			&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
				&lt;p&gt;
					But consider that this is a choice I have to make – do I spend 50% more on an apartment
					and stress my budget to death, or make my wife and kids in a place where I have 5 front
					doors within 15 feet of ours in the murder-iest part of town? Neither choice is great, but
					the lack of intermediate choices forces you to do one or the other.
				&lt;/p&gt;
				&lt;cite&gt;
					&lt;a href=&quot;https://residentcontrarian.substack.com/p/on-the-experience-of-being-poor-ish&quot;&gt;
						&quot;On the experience of being poor-ish, for people who aren&apos;t&quot;
					&lt;/a&gt;
				&lt;/cite&gt;
			&lt;/blockquote&gt;

			&lt;p&gt;
				I think it’s really hard for many people to wrap their head around just how difficult it can
				be to be in that sort of situation. And it seems to be even harder to make your way out of
				it.
			&lt;/p&gt;

			&lt;p&gt;
				I find this an especially healthy read for myself. I wouldn’t classify myself as rich, but
				I’m fortunate enough to have never had any financial troubles in my life. It’s difficult for
				me to truly understand what it’s like to be so poor.
			&lt;/p&gt;

			&lt;p&gt;
				Learning more about the struggles of being so incredibly close to not being able to provide
				for yourself and your family is incredibly humbling. It makes me feel extremely thankful for
				how lucky I am. For how lucky I’ve been.
			&lt;/p&gt;

			&lt;p&gt;
				I also wonder how we can bring about lasting change? No one should be forced to make a
				choice between the ability to work or feeding your family; between food or water; between
				starving or dying of thirst.
			&lt;/p&gt;
</description>
        <link>https://reykjalin.org/blog/poor-ish/</link>
        <pubDate>Tue, 02 Mar 2021 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/poor-ish/</guid>
      </item>
    
      <item>
        <title>How do you share photos with your family?</title>
        <description>&lt;p&gt;
				For the last several years I’ve been living far, far away from my family, and — as is to be
				expected — they want updates! Photos and such. &lt;em&gt;Moments&lt;/em&gt;. I’ve never really been any
				good at sharing those, but I would at least posted something &lt;em&gt;occasionally&lt;/em&gt; while I
				had Facebook and Instagram accounts. Now that I’ve deleted those accounts I’m not sure how
				to go about sharing moments from my life with them.
			&lt;/p&gt;

			&lt;p&gt;
				Another aspect to this is that I don’t receive any updates from them. If my wife didn’t have
				Instagram and Facebook I’d have no idea what was going on, except for news arriving through
				my parents.
			&lt;/p&gt;

			&lt;p&gt;
				I could, of course, do something like sending out a newsletter every month, adding photos to
				some shared cloud service, or set up a different blog that’s only intended for family. But
				that’s mostly one-way communication. There’s no sense of sharing things with
				&lt;em&gt;your family&lt;/em&gt;. They might as well be random people on the internet.
			&lt;/p&gt;

			&lt;p&gt;
				I’m not a big fan of those solutions. I just want to share some photos and videos with a
				short caption. Maybe a quick status update every now and then. Does that really have to be
				this hard?
			&lt;/p&gt;

			&lt;p&gt;
				I guess I could create profiles on either Facebook or Instagram (&lt;em&gt;ew&lt;/em&gt; 🤮).
				“Traditional” social media would certainly make this easier, but I don’t want to use social
				media to share personal moments in my life. I just want to share things with my friends and
				family.
			&lt;/p&gt;

			&lt;p&gt;
				There are things like &lt;a href=&quot;https://pixelfed.org&quot;&gt;Pixelfed&lt;/a&gt; that aim to replace
				services like Instagram with a more privacy friendly alternative. I even set up an instance
				to try it out, and that’s been going pretty well, even though the user experience is clunky
				right now.
			&lt;/p&gt;

			&lt;p&gt;
				My problem, however, with Pixelfed is that it’s trying &lt;em&gt;to be&lt;/em&gt; Instagram. Part of the
				mission statement there is discovery; other people can view all your public posts. It’s an
				open platform where anyone can follow anyone. You don’t even need an account to see posts.
			&lt;/p&gt;

			&lt;p&gt;
				I want something private, &lt;em&gt;something personal&lt;/em&gt;; just for my friends and family. I
				don’t care about the thoughts of the world. Quite frankly I don’t even want to share these
				moments with the rest of the world. Why would I?
			&lt;/p&gt;

			&lt;p&gt;
				Another side to this is that &lt;em&gt;I don’t care&lt;/em&gt; about likes or notifications or boosts or
				retweets or favorites or “hey, you might know this person!”. Comments and discussion can be
				fun, but I could do without all that other &lt;em&gt;fluff&lt;/em&gt;.
			&lt;/p&gt;

			&lt;p&gt;
				I just want to share some photos and videos with the people closest to me, and maybe have a
				quick chat with them about those moments. But there’s just no good way to do that.
			&lt;/p&gt;

			&lt;p&gt;Maybe something will come along soon. I wouldn’t bet on it, but who knows?&lt;/p&gt;

			&lt;p&gt;
				How do you, dear reader, approach this? How do you let your family know what’s up with you?
			&lt;/p&gt;
</description>
        <link>https://reykjalin.org/blog/family-photos/</link>
        <pubDate>Wed, 20 Jan 2021 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/family-photos/</guid>
      </item>
    
      <item>
        <title>How to fix blurry text in macOS applications</title>
        <description>&lt;p&gt;
				macOS is my operating system of choice, so when I’m working on projects that use a
				&lt;a href=&quot;https://en.wikipedia.org/wiki/Graphical_user_interface&quot;&gt;GUI&lt;/a&gt; I like to build
				&lt;a href=&quot;https://en.wikipedia.org/wiki/Bundle_(macOS)&quot;&gt;macOS application bundles&lt;/a&gt;. This
				allows me to have some flexibility when it comes to running the app. However, every time I
				run these bundles for the first time the text in the app is blurry, and I keep forgetting
				why that happens.
			&lt;/p&gt;

			&lt;p&gt;
				This problem will inevitably come up again, so I’m documenting the solution here. I can’t
				say I’m very interested in spending hours searching for a solution. Again. &lt;em&gt;Sigh&lt;/em&gt;.
			&lt;/p&gt;

			&lt;h2&gt;Solution: support high resolution displays&lt;/h2&gt;

			&lt;p&gt;Add the following to your &lt;code&gt;Info.plist&lt;/code&gt;&lt;/p&gt;

			&lt;pre class=&quot;language-xml&quot;&gt;&lt;code&gt;&amp;lt;key&amp;gt;NSHighResolutionCapable&amp;lt;/key&amp;gt;
&amp;lt;true/&amp;gt;&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;such that the file will look something like this&lt;/p&gt;

			&lt;pre class=&quot;language-xml&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&amp;gt;
&amp;lt;plist version=&quot;1.0&quot;&amp;gt;
&amp;lt;dict&amp;gt;
	&amp;lt;key&amp;gt;NSHighResolutionCapable&amp;lt;/key&amp;gt;
	&amp;lt;true/&amp;gt;
&amp;lt;/dict&amp;gt;
&amp;lt;/plist&amp;gt;&lt;/code&gt;&lt;/pre&gt;

			&lt;p&gt;
				There will be other items in the file, but that’s where you want the key-value pair to be;
				in the &lt;code&gt;dict&lt;/code&gt; object.
			&lt;/p&gt;

			&lt;p&gt;
				If you’re using &lt;a href=&quot;https://github.com/pyinstaller/pyinstaller/&quot;&gt;PyInstaller&lt;/a&gt; like I
				currently am, you can add this to the &lt;code&gt;info_plist&lt;/code&gt; dict in the specification
				file:
			&lt;/p&gt;

			&lt;pre class=&quot;language-python&quot;&gt;&lt;code&gt;app = BUNDLE(
	info_plist={
		&quot;NSHighResolutionCapable&quot;: True
	}
)&lt;/code&gt;&lt;/pre&gt;

			&lt;h2&gt;Why is this necessary?&lt;/h2&gt;

			&lt;p&gt;
				macOS uses the
				&lt;a href=&quot;https://developer.apple.com/documentation/bundleresources/information_property_list&quot;&gt;
					Information Property List
				&lt;/a&gt;
				(&lt;code&gt;Info.plist&lt;/code&gt;) to identify various capabilities of the app in question, including
				whether your app supports high resolution displays via the
				&lt;a href=&quot;https://developer.apple.com/documentation/bundleresources/information_property_list/nshighresolutioncapable&quot;&gt;
					NSHighResolutionCapable property
				&lt;/a&gt;.
				I imagine the text looks blurry because macOS renders the app at low resolutions when the
				property is not defined or is set to &lt;code&gt;False&lt;/code&gt;.
			&lt;/p&gt;

			&lt;p&gt;
				So by setting the property to &lt;code&gt;True&lt;/code&gt; you’re letting macOS know that “hey it’s ok
				to render this app at higher resolutions”. Once the app is rendered at a higher resolution
				the text will no longer be blurry! Success!
			&lt;/p&gt;
</description>
        <link>https://reykjalin.org/blog/blurry-macos-text/</link>
        <pubDate>Thu, 17 Sep 2020 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/blurry-macos-text/</guid>
      </item>
    
      <item>
        <title>The End of History</title>
        <description>&lt;p&gt;In “&lt;a href=&quot;https://www.youtube.com/watch?v=CxkvsrSUyOU&quot; target=&quot;_blank&quot;&gt;The End of History&lt;/a&gt;” [&lt;a href=&quot;https://yewtu.be/watch?v=CxkvsrSUyOU&quot; target=&quot;_blank&quot;&gt;non-YouTube link&lt;/a&gt;] &lt;a href=&quot;https://www.kylehill.net/&quot; target=&quot;_blank&quot;&gt;Kyle Hill&lt;/a&gt; talks about how we live on the brink of extinction to prevent an all-out nuclear war. I found the video to be an interesting, and thoughtful, take on this weird situation we find ourselves in, and the whole &lt;a href=&quot;https://en.wikipedia.org/wiki/Mutual_assured_destruction&quot; target=&quot;_blank&quot;&gt;mutually assured destruction&lt;/a&gt; doctrine.&lt;/p&gt;&lt;p&gt;It’s both amazing to think about the power we as a species wield, and terrifying to think about how easy it would be to cause our own extinction. And I don’t think there’s a word strong enough to describe the terror I feel when thinking about how close we’ve come &lt;em&gt;several&lt;/em&gt; times. Governments around the world need to think really, &lt;em&gt;really&lt;/em&gt;, hard about whether this is sustainable.&lt;/p&gt;&lt;p&gt;Is whatever conflict that happens to be “the biggest threat of your time™” really worth not just your and your opponent’s existence, but also the majority of life on earth?&lt;/p&gt;</description>
        <link>https://reykjalin.org/blog/the-end-of-history/</link>
        <pubDate>Sat, 15 Aug 2020 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/the-end-of-history/</guid>
      </item>
    
      <item>
        <title>Build your own text editor with Reason — Getting started</title>
        <description>&lt;div class=&quot;block notice warning&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;block title&quot;&gt;&lt;p&gt; Warning!&lt;/p&gt;&lt;/div&gt;&lt;p&gt;As of September 29, 2020 I’ve decided to put this project on hold for a bit, until the Revery team addresses &lt;a href=&quot;https://github.com/revery-ui/revery/issues/287&quot;&gt;an issue&lt;/a&gt; with their &lt;code&gt;ScrollView&lt;/code&gt; component. While it’s not a showstopper, the UI wouldn’t really work without it. I’ll be back on this as soon as the issue is addressed!&lt;/p&gt;&lt;/div&gt;&lt;p&gt;I’ve been thinking a lot about text editors lately, going so far as to playing with &lt;a href=&quot;https://reykjalin.org/blog/new-code-editor/&quot;&gt;writing my own&lt;/a&gt;. In the process of exploring that idea I came across a tutorial written by &lt;a href=&quot;https://viewsourcecode.org/&quot; target=&quot;_blank&quot;&gt;Pailey Quilts&lt;/a&gt; on how to &lt;a href=&quot;https://viewsourcecode.org/snaptoken/kilo/index.html&quot; target=&quot;_blank&quot;&gt;build a text editor&lt;/a&gt; in C. It’s a great write-up that thoroughly explains the code behind &lt;a href=&quot;https://github.com/antirez/kilo&quot; target=&quot;_blank&quot;&gt;kilo&lt;/a&gt;, a simple terminal editor.&lt;/p&gt;&lt;p&gt;I’ve been writing code in &lt;a href=&quot;https://reasonml.github.io&quot; target=&quot;_blank&quot;&gt;Reason&lt;/a&gt; in different projects lately and I’m quickly falling in love with the language. I find the developer experience very pleasing, and I absolutely love the type system. I’ve also been keeping my eyes on &lt;a href=&quot;https://v2.onivim.io&quot; target=&quot;_blank&quot;&gt;Onivim 2&lt;/a&gt;, a new text editor that aims to have the usability of VSCode with the utility of Vim — an ambitious project to say the least!&lt;/p&gt;&lt;p&gt;It just so happens that Onivim 2 is built with Reason! More specifically, it’s built with &lt;a href=&quot;https://www.outrunlabs.com/revery/&quot; target=&quot;_blank&quot;&gt;Revery&lt;/a&gt;, a GUI framework that came to be &lt;em&gt;because of&lt;/em&gt; Onivim 2. And all of this got me thinking; what would a kilo-like editor look like when made with Reason and Revery?&lt;/p&gt;&lt;p&gt;So, here we are. My take on Pailey’s tutorial, except instead of writing a simple terminal editor in C, you’re going to write a simple GUI editor in Reason!&lt;/p&gt;&lt;div class=&quot;block info notice&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;block title&quot;&gt;&lt;p&gt; Tip!&lt;/p&gt;&lt;/div&gt;&lt;p&gt;All the code and incremental changes will be published in &lt;a href=&quot;https://git.sr.ht/~reykjalin/kilo-gui&quot;&gt;~reykjalin/kilo-gui&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;h2&gt;Setup&lt;/h2&gt;&lt;p&gt;Just like in Pailey’s post, the first thing you’ll want to do is to set up your environment. To build GUIs using Reason and Revery, you’ll need to install &lt;a href=&quot;https://esy.sh/&quot; target=&quot;_blank&quot;&gt;Esy&lt;/a&gt;. Esy is a package manager and build tool, similar to &lt;a href=&quot;https://nodejs.org/en/&quot; target=&quot;_blank&quot;&gt;Node’s&lt;/a&gt; &lt;code&gt;npm&lt;/code&gt;, but built for Reason and OCaml.&lt;/p&gt;&lt;h3&gt;How to install Esy&lt;/h3&gt;&lt;p&gt;The first thing you’ll need is &lt;a href=&quot;https://nodejs.org/en/&quot; target=&quot;_blank&quot;&gt;Node&lt;/a&gt;. The LTS version will do just fine. Node should come with &lt;code&gt;npm&lt;/code&gt;, and it’s really &lt;code&gt;npm&lt;/code&gt; you need. You’ll use &lt;code&gt;npm&lt;/code&gt; to install &lt;code&gt;esy&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;constant operator function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant function&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant property function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;--global&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;esy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may need to run this command as an administrator, in which case you’ll need to add the &lt;code&gt;--unsafe-perm=true&lt;/code&gt; flag.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;constant operator function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant function&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant property function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;--global&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;esy&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;--unsafe-perm=true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’d recommend you try running the test project they’ve set up to make sure &lt;code&gt;esy&lt;/code&gt; is installed correctly. You can find instructions on how to do this in Esy’s &lt;a href=&quot;https://esy.sh/docs/en/getting-started.html&quot; target=&quot;_blank&quot;&gt;getting started guide&lt;/a&gt;.&lt;/p&gt;&lt;h3&gt;Creating a Revery project&lt;/h3&gt;&lt;p&gt;The simplest way to get started with a new Revery project is to clone the &lt;a href=&quot;https://github.com/revery-ui/revery-quick-start&quot; target=&quot;_blank&quot;&gt;Revery quick start&lt;/a&gt; project, and go from there.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;constant operator function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant function&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant property function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;clone&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;https://github.com/revery-ui/revery-quick-start.git&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’ve found that the dependencies tend to be out of date here, and there might be some key differences in the Revery API between the version installed in the quick start vs. the latest version of Revery, so the first thing you’re going to do is update all the project dependencies.&lt;/p&gt;&lt;h3&gt;Updating the project dependencies&lt;/h3&gt;&lt;p&gt;The current dependencies can be found in &lt;code&gt;package.json&lt;/code&gt; in the &lt;code&gt;dependencies&lt;/code&gt; and &lt;code&gt;devDependencies&lt;/code&gt; objects. The dependencies should look something like this&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;json&quot;&gt;&lt;span class=&quot;comment&quot;&gt;/* package.json */&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;&amp;quot;dependencies&amp;quot;&lt;/span&gt;: {
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;revery&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;revery-ui/revery#8fd380c&amp;quot;&lt;/span&gt;,
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;@opam/dune&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;2.5.0&amp;quot;&lt;/span&gt;,
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;@glennsl/timber&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;^1.2.0&amp;quot;&lt;/span&gt;,
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;esy-macdylibbundler&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;*&amp;quot;&lt;/span&gt;
},
&lt;span class=&quot;string&quot;&gt;&amp;quot;devDependencies&amp;quot;&lt;/span&gt;: {
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;ocaml&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;~4.9.0&amp;quot;&lt;/span&gt;,
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;@opam/ocaml-lsp-server&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;ocaml/ocaml-lsp:ocaml-lsp-server.opam#04733ed&amp;quot;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finding the newest versions for your dependencies can be a bit tricky, so here is how I found the newest versions:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/revery-ui/revery&quot; target=&quot;_blank&quot;&gt;Revery&lt;/a&gt;‘s latest commit hash is &lt;code&gt;1531d5f&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://opam.ocaml.org/packages/dune&quot; target=&quot;_blank&quot;&gt;dune&lt;/a&gt;‘s latest version is &lt;code&gt;2.6.1&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ocaml.org/docs/install.html&quot; target=&quot;_blank&quot;&gt;OCaml&lt;/a&gt;‘s latest version is &lt;code&gt;4.10.0&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/ocaml/ocaml-lsp&quot; target=&quot;_blank&quot;&gt;ocaml-lsp&lt;/a&gt;‘s latest commit hash is &lt;code&gt;b017b14&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/glennsl/timber&quot; target=&quot;_blank&quot;&gt;Timber&lt;/a&gt; is already using the latest version.&lt;/li&gt;&lt;li&gt;The &lt;code&gt;*&lt;/code&gt; in &lt;a href=&quot;https://www.npmjs.com/package/esy-macdylibbundler&quot; target=&quot;_blank&quot;&gt;esy-macdylibbundler&lt;/a&gt; means the latest version will be installed.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;So after we update &lt;code&gt;package.json&lt;/code&gt; the dependencies should look something like this.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;json&quot;&gt;&lt;span class=&quot;comment&quot;&gt;/* package.json */&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;&amp;quot;dependencies&amp;quot;&lt;/span&gt;: {
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;revery&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;revery-ui/revery#1531d5f&amp;quot;&lt;/span&gt;,
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;@opam/dune&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;2.6.1&amp;quot;&lt;/span&gt;,
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;@glennsl/timber&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;^1.2.0&amp;quot;&lt;/span&gt;,
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;esy-macdylibbundler&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;*&amp;quot;&lt;/span&gt;
},
&lt;span class=&quot;string&quot;&gt;&amp;quot;devDependencies&amp;quot;&lt;/span&gt;: {
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;ocaml&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;~4.10.0&amp;quot;&lt;/span&gt;,
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;@opam/ocaml-lsp-server&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;ocaml/ocaml-lsp:ocaml-lsp-server.opam#b017b14&amp;quot;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Updating the project metadata&lt;/h3&gt;&lt;p&gt;Updating the project metadata to fit your needs is usually a good idea. You should use this chance to do exactly that! You can find this project metadata in &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;json&quot;&gt;&lt;span class=&quot;comment&quot;&gt;/* package.json */&lt;/span&gt;
{
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;kilo-gui&amp;quot;&lt;/span&gt;,
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;version&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;0.1.0&amp;quot;&lt;/span&gt;,
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;description&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;A simple GUI editor built with Revery&amp;quot;&lt;/span&gt;,
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;license&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;MIT&amp;quot;&lt;/span&gt;,
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;scripts&amp;quot;&lt;/span&gt;: {
		&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;format&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;bash -c &lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;refmt --in-place **/*.re&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;,
		&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;run&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;esy x Kilo&amp;quot;&lt;/span&gt;
	},
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;esy&amp;quot;&lt;/span&gt;: {
		&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;build&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;dune build -p Kilo&amp;quot;&lt;/span&gt;,
		&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;buildDev&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;refmterr dune build -p Kilo&amp;quot;&lt;/span&gt;,
		&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;buildsInSource&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;_build&amp;quot;&lt;/span&gt;
	},
	&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;revery-packager&amp;quot;&lt;/span&gt;: {
		&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;bundleName&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;Kilo&amp;quot;&lt;/span&gt;,
		&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;bundleId&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;com.thorlaksson.kilo&amp;quot;&lt;/span&gt;,
		&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;displayName&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;Kilo&amp;quot;&lt;/span&gt;,
		&lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;mainExecutable&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;Kilo&amp;quot;&lt;/span&gt;,
		&lt;span class=&quot;comment&quot;&gt;// ...&lt;/span&gt;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The changes I’ve suggested here mean you need to rename &lt;code&gt;src/App.re&lt;/code&gt; to &lt;code&gt;src/Kilo.re&lt;/code&gt;, and change some of the &lt;code&gt;dune&lt;/code&gt; files to accommodate these project changes. It’s a bit tedious to list all of those here, so I’d recommend you look at &lt;a href=&quot;https://git.sr.ht/~reykjalin/kilo-gui/commit/6de5687cbf3c9dc41261235c07ce60bda40d876e&quot; target=&quot;_blank&quot;&gt;the commit&lt;/a&gt; where I make these changes instead.&lt;/p&gt;&lt;h2&gt;Building the project&lt;/h2&gt;&lt;p&gt;The first build after updating your dependencies needs to be completely clean, so before doing anything you should delete the &lt;code&gt;esy.lock&lt;/code&gt; directory, which is used to lock the dependency versions in place. If you don’t delete this directory now, &lt;code&gt;esy&lt;/code&gt; will not update the dependencies, despite the changes in &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;constant operator function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant function&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant property function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-rf&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;esy.lock&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can build the project by running &lt;code&gt;esy&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;constant operator function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant function&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant property function&quot;&gt;esy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running &lt;code&gt;esy&lt;/code&gt; will install your dependencies and build your project. This first build will take a while since &lt;code&gt;esy&lt;/code&gt; will actually build all the dependencies for you. &lt;code&gt;esy&lt;/code&gt; caches your built dependencies so subsequent builds will be close to instantaneous. You just need to suffer through this long build time the first time you build your project.&lt;/p&gt;&lt;p&gt;And now you can run the project!&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;constant operator function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant function&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant property function&quot;&gt;esy&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;block info notice&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;block title&quot;&gt;&lt;p&gt; Tip!&lt;/p&gt;&lt;/div&gt;&lt;p&gt;This command will both build and run the project for you! No need to run 2 commands, yay! 🎉&lt;/p&gt;&lt;/div&gt;&lt;p&gt;And you should be greeted with the default quick start app!&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://reykjalin.org/blog/reason-editor-part-1/revery-quick-start.webp&quot; alt=&quot;The default quick start app for the Revery GUI framework. Displays a window with 3 elements in the center. First element is text that says &quot;Welcome to Revery&quot;. Second element is a button that says &quot;Increment&quot;. Third element is text that says &quot;Times clicked: 0&quot;, and is intended to show the number of times the button has been clicked.&quot;&gt;&lt;/p&gt;&lt;h2&gt;Make room for your own work&lt;/h2&gt;&lt;p&gt;The last thing for you to do is &lt;a href=&quot;https://git.sr.ht/~reykjalin/kilo-gui/commit/c64ab7c0f87e0daff47234f4af75c21026fe2c94&quot; target=&quot;_blank&quot;&gt;remove all the unnecessary code&lt;/a&gt;, and start with a clean slate. This is relatively simple; delete &lt;code&gt;src/AnimatedText.re&lt;/code&gt;, &lt;code&gt;src/SimpleButton.re&lt;/code&gt;, and &lt;code&gt;src/Theme.re&lt;/code&gt;, and then modify &lt;code&gt;src/Kilo.re&lt;/code&gt;. to remove the unnecessary &lt;code&gt;Style&lt;/code&gt;, and reduce the main app component to an empty component.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/* src/Kilo.re */
let main = () =&amp;gt; React.empty;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, you need to remove the dependency on &lt;code&gt;Theme.re&lt;/code&gt; by changing the &lt;code&gt;win&lt;/code&gt; variable to&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/* src/Kilo.re */
let win =
	App.createWindow(
	app,
	&amp;quot;Kilo&amp;quot;,
	~createOptions=
		WindowCreateOptions.create(
		~backgroundColor=Colors.white,
		~width=512,
		~height=384,
		(),
		),
	);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now when you run the program you should see an empty window—a great place to start a new project!&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://reykjalin.org/blog/reason-editor-part-1/kilo-empty.webp&quot; alt=&quot;An empty window.&quot;&gt;&lt;/p&gt;&lt;p&gt;Going forward you’ll want to build and run the app to see the changes in action and make sure you didn’t accidentally break something. Like Pailey says:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;It is easy to forget to recompile, and just run &lt;code&gt;./kilo&lt;/code&gt;, and wonder why your changes to &lt;code&gt;kilo.c&lt;/code&gt; don’t seem to have any effect. You must recompile in order for changes in &lt;code&gt;kilo.c&lt;/code&gt; to be reflected in &lt;code&gt;kilo&lt;/code&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The same can be said here! Make sure to run the app after making changes!&lt;/p&gt;&lt;div class=&quot;block center&quot;&gt;&lt;p&gt; &lt;em&gt;The next post will be up soon!&lt;/em&gt;&lt;/p&gt;&lt;/div&gt;</description>
        <link>https://reykjalin.org/blog/reason-editor-part-1/</link>
        <pubDate>Mon, 27 Jul 2020 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/reason-editor-part-1/</guid>
      </item>
    
      <item>
        <title>I&apos;m changing the default branch name in my git repositories, and you should too</title>
        <description>&lt;p&gt;There’s been a bit of an uproar in the software development community because it’s been brought to everyone’s attention that the default branch naming convention of &lt;code&gt;master&lt;/code&gt; in git &lt;a href=&quot;https://mail.gnome.org/archives/desktop-devel-list/2019-May/msg00066.html&quot; target=&quot;_blank&quot;&gt;isn’t exactly appropriate&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;This was first brought to my attention at work with a link to a &lt;a href=&quot;https://www.hanselman.com/blog/EasilyRenameYourGitDefaultBranchFromMasterToMain.aspx&quot; target=&quot;_blank&quot;&gt;post from Scott Hanselman&lt;/a&gt; about renaming your default git branch in response to the &lt;a href=&quot;https://tools.ietf.org/id/draft-knodel-terminology-00.html#rfc.section.1.1&quot; target=&quot;_blank&quot;&gt;IETF pointing out&lt;/a&gt; that the master-slave terminology is both “inappropriate and arcane” as well as “technically and historically inaccurate”.&lt;/p&gt;&lt;p&gt;Many teams at work have decided to change the default branch names in their repositories, and I’m pretty sure that will end up being a company-wide change. Even &lt;a href=&quot;https://www.zdnet.com/article/github-to-replace-master-with-alternative-term-to-avoid-slavery-references/&quot; target=&quot;_blank&quot;&gt;GitHub is planning to change the default branch&lt;/a&gt; to &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Having thought about this for a while I wholeheartedly agree with this decision, and I will be changing the default branch names in my own repositories to &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;But before we go any further let’s take a look at what the actual problem with the status quo is.&lt;/p&gt;&lt;h2&gt;What’s the problem with &lt;code&gt;master&lt;/code&gt;?&lt;/h2&gt;&lt;p&gt;As far as I’ve been able to gather, &lt;a href=&quot;https://mail.gnome.org/archives/desktop-devel-list/2019-May/msg00066.html&quot; target=&quot;_blank&quot;&gt;the origin of the default branch name in git&lt;/a&gt; comes from a different version control system called BitKeeper. BitKeeper’s naming convention &lt;a href=&quot;https://github.com/bitkeeper-scm/bitkeeper/blob/5695c0d0ecd062f13542c3cb04dd872466774fbf/doc/HOWTO.ask#L223-L237&quot; target=&quot;_blank&quot;&gt;is based on the master-slave relationship&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;This is problematic because of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Master/slave_(technology)#Terminology_concerns&quot; target=&quot;_blank&quot;&gt;racist subtext&lt;/a&gt; inherent in that metaphor, the historical context of which should never be forgotten.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The use of such terms [racist language] does not merely reflect a racist culture, but also serves to legitimize and perpetuate it.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6148600/&quot; target=&quot;_blank&quot;&gt;https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6148600/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I think it’s pretty safe to say that no one intends to legitimize and perpetuate racism, but just by using the language you become part of the problem, no matter whether that’s intentional or not.&lt;/p&gt;&lt;p&gt;I have no interest in being part of a community that accepts and encourages racist language—and racism by proxy—and neither should you.&lt;/p&gt;&lt;h2&gt;So how do we address this?&lt;/h2&gt;&lt;p&gt;The first thing we as a community need to do is recognize that there’s a problem here. Only then can we start to work our way through the issue, and come up with and implement solutions.&lt;/p&gt;&lt;p&gt;In this particular case the solution is easy. Just change the default branch name in your repositories. Stop using master-slave terminology. Try your best to stop using racist language in your day-to-day life, accidental or otherwise.&lt;/p&gt;&lt;p&gt;This will, of course, take a bit of getting used to, but I’m 100% sure that’s something everyone will get over quickly.&lt;/p&gt;&lt;p&gt;&lt;code&gt;main&lt;/code&gt;, &lt;code&gt;primary&lt;/code&gt;, &lt;code&gt;default&lt;/code&gt;, &lt;code&gt;production&lt;/code&gt;, &lt;code&gt;leader&lt;/code&gt;, &lt;code&gt;active&lt;/code&gt; are all reasonable alternatives. Having thought on this a bit I’d argue that &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;primary&lt;/code&gt;, and &lt;code&gt;default&lt;/code&gt; all describe the general purpose of the default branch much better than &lt;code&gt;master&lt;/code&gt; ever can. Those terms don’t rely on metaphors; they describe the actual function.&lt;/p&gt;&lt;p&gt;The IETF has &lt;a href=&quot;https://tools.ietf.org/id/draft-knodel-terminology-00.html#rfc.section.1.1.1&quot; target=&quot;_blank&quot;&gt;more suggestions&lt;/a&gt; that are appropriate in other contexts.&lt;/p&gt;&lt;h2&gt;Counter-arguments&lt;/h2&gt;&lt;p&gt;In my opinion, all of the arguments against this change ignore the fact that there exist better alternatives that describe the function of the default branch in a much better way than &lt;code&gt;master&lt;/code&gt; can.&lt;/p&gt;&lt;p&gt;They also seem to ignore that this change is small, and will only need a slight adjustment on your part. For people that already find the &lt;code&gt;master&lt;/code&gt; branch offensive this will be a huge change for the better.&lt;/p&gt;&lt;p&gt;Weighing those two against each other should make the choice obvious. The tiny bit of discomfort this will be for you is entirely worth the gain this will be for someone else.&lt;/p&gt;&lt;p&gt;But let’s discuss some of the actual counter-arguments I’ve seen floating around.&lt;/p&gt;&lt;h3&gt;Many tools rely on &lt;code&gt;master&lt;/code&gt; as the default branch&lt;/h3&gt;&lt;p&gt;No, they probably don’t. I’ve seen people say this, and this worried me too. However, I’ve yet to see a tool that breaks when the default branch isn’t &lt;code&gt;master&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Let’s all be honest here. If the tool breaks when you change the default branch, it frankly needs a lot more work, and you probably shouldn’t be using it for your super important project.&lt;/p&gt;&lt;h3&gt;The &lt;code&gt;master&lt;/code&gt; branch isn’t related to slavery, but a “master copy” from the recording industry&lt;/h3&gt;&lt;p&gt;At this point the origin of the term no longer matters. Even if the master-slave metaphor wasn’t the original intent behind the phrasing, the fact of the matter is that the metaphor is now associated with the &lt;code&gt;master&lt;/code&gt; branch naming convention.&lt;/p&gt;&lt;h3&gt;Avoiding the use of &lt;code&gt;master&lt;/code&gt; will only give more meaning to the racist undertones of the word&lt;/h3&gt;&lt;p&gt;Here’s a newsflash for you: &lt;em&gt;the word is already associated with racism&lt;/em&gt;. Using the word will not neutralize that association.&lt;/p&gt;&lt;p&gt;Try applying this reasoning to other racially charged words, especially ones that are already widely accepted to be racist. Is this &lt;em&gt;really&lt;/em&gt; an argument you want to make?&lt;/p&gt;&lt;h3&gt;Changing the branch name won’t change society&lt;/h3&gt;&lt;p&gt;No, it probably won’t. But then again, that’s not the point of the change.&lt;/p&gt;&lt;p&gt;Language and terminology with offensive subtexts directly affects people’s perception of the content. As such, we shouldn’t be using language with offensive subtexts. It’s that simple.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Complex and subtle configurations of sexist, racist, or ethnocentric language use in technical documents can derail or &lt;em&gt;interfere with readers’ ability and desire to comprehend and follow important information&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://doi.org/10.1080/10572259809364639&quot; target=&quot;_blank&quot;&gt;https://doi.org/10.1080/10572259809364639&lt;/a&gt; (emphasis mine)&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The point of this change is to make the software development community more welcoming to all, no matter what their background is.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;If changing the default branch name will make even one person feel more welcomed and comfortable, then the change will have been worth it.&lt;/strong&gt;&lt;/p&gt;&lt;h2&gt;Final words&lt;/h2&gt;&lt;p&gt;This small change could make someone’s day. It could make them ecstatic. This might be something that’s been causing them discomfort for a long time. Think about that someone. Isn’t a bit of &lt;em&gt;temporary&lt;/em&gt; discomfort a tiny price to pay for that someone’s happiness?&lt;/p&gt;&lt;p&gt;Do the right thing.&lt;/p&gt;&lt;p&gt;Be the change you wish to see in the world.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;We but mirror the world. All the tendencies present in the outer world are to be found in the world of our body. If we could change ourselves, the tendencies in the world would also change. As a man changes his own nature, so does the attitude of the world change towards him. This is the divine mystery supreme. A wonderful thing it is and the source of our happiness. We need not wait to see what others do.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Mahatma Gandhi&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
        <link>https://reykjalin.org/blog/git-default-branch/</link>
        <pubDate>Thu, 25 Jun 2020 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/git-default-branch/</guid>
      </item>
    
      <item>
        <title>A new code editor?</title>
        <description>&lt;p&gt;I’ve written before about how &lt;a href=&quot;https://reykjalin.org/blog/no-good-code-editor/&quot;&gt;I’m not happy&lt;/a&gt; with the code editors available today. I think they can be improved upon, so I’ve started experimenting with making my own. I don’t think this will ever be the shiny new code editor on the block, but I’m hoping it’ll at least be something &lt;em&gt;I’d&lt;/em&gt; prefer over other editors.&lt;/p&gt;&lt;p&gt;I’m currently calling this project &lt;a href=&quot;https://sr.ht/~reykjalin/Qode&quot; target=&quot;_blank&quot;&gt;Qode&lt;/a&gt;, but I might change that further down the line—especially since I’ve already seen other similar projects with the same name.&lt;/p&gt;&lt;p&gt;I’m not sure how far I’ll take this, and might not even make this a huge thing, but I want to outline part of the vision for this editor. I think doing so will help me stay focused on what to work on next, and whether I’m spending too much time digging into something I don’t need to think about.&lt;/p&gt;&lt;p&gt;So, without further ado, let’s get into it.&lt;/p&gt;&lt;h2&gt;Graphical interface please!&lt;/h2&gt;&lt;p&gt;I dislike the limitations of terminal editors, and as such I want the editor to have a graphical interface (&lt;a href=&quot;https://en.wikipedia.org/wiki/Graphical_user_interface&quot; target=&quot;_blank&quot;&gt;GUI&lt;/a&gt;). That said, I also want this interface to be minimalistic. Most of the time it shouldn’t be showing you anything beyond what a terminal editor would show you.&lt;/p&gt;&lt;p&gt;What I mean by this is that most of the time the only thing visible in the editor should be the text editing window itself. No toolbars, no sidebars, no fluff.&lt;/p&gt;&lt;p&gt;That doesn’t mean those won’t be available, mind you! Just that they shouldn’t be visible by default. Instead, you should be able to bring them to focus with a keyboard shortcut (e.g. double-tap tab, hold shift, etc.) or a single mouse-click/hover action.&lt;/p&gt;&lt;p&gt;I want the editor to get out of the way when you’re writing code, and I think modern editors fail when it comes to this, what with all their sidebars and toolbars.&lt;/p&gt;&lt;h2&gt;Keyboard driven&lt;/h2&gt;&lt;p&gt;I want the GUI to be fully accessible with just a keyboard. Or at the very least, you should be able to control every aspect of the editor with a keyboard.&lt;/p&gt;&lt;p&gt;I’m not really talking about navigating menus with a keyboard (although that should be possible either way), instead I’m thinking of a sort of command mode, similar to &lt;a href=&quot;https://www.vim.org/&quot; target=&quot;_blank&quot;&gt;Vim&lt;/a&gt;, &lt;a href=&quot;http://www.gnu.org/software/emacs/&quot; target=&quot;_blank&quot;&gt;Emacs&lt;/a&gt;, and &lt;a href=&quot;https://kakoune.org/&quot; target=&quot;_blank&quot;&gt;Kakoune&lt;/a&gt;. The commands should allow you to do anything you might otherwise do by navigating through a bunch of GUI menus.&lt;/p&gt;&lt;p&gt;Taking Vim as an example here, you enter command mode by typing &lt;code&gt;:&lt;/code&gt; while in normal mode. You can then type &lt;code&gt;write&lt;/code&gt; and hit the return key, and that’s how you save the file you’re currently working on; by typing &lt;code&gt;:write&amp;lt;ret&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;h2&gt;Mouse driven&lt;/h2&gt;&lt;p&gt;Yes! Also fully mouse driven!&lt;/p&gt;&lt;p&gt;I want to be able to fully navigate the editor with only my mouse. Sometimes I like doing that, e.g. when I’m just browsing through the codebase, or messing with some settings.&lt;/p&gt;&lt;p&gt;At other times I’d like to be able to use the keyboard, e.g. when I’m already working on some code and I want to quickly switch to a different file, jump to a definition, etc.&lt;/p&gt;&lt;p&gt;And sometimes I have one hand on the keyboard and the other on my mouse. That should limit me as little as possible.&lt;/p&gt;&lt;p&gt;The whole goal here is to make the editor equally accessible with multiple input methods.&lt;/p&gt;&lt;h2&gt;Functionality driven by plugins&lt;/h2&gt;&lt;p&gt;This is probably the most difficult part to design and implement. I want to allow plugins to control every aspect of the editor; rebind keys and actions, open/close dialogs, add configuration options, etc.&lt;/p&gt;&lt;p&gt;The editor should have default functionality relevant to the core usage of a code editor, but every aspect of that default functionality should be extendable with plugins. The system should even allow you to completely re-implement how some things work.&lt;/p&gt;&lt;p&gt;The example that’s at the forefront of my mind all the time is rebinding keys. Or rather, changing the input handling. It should be easy for plugins to create modal editing modes, like what Vim and Kakoune do. That means you’re not just rebinding keys, the plugin should be able to change the whole input handling for the editor.&lt;/p&gt;&lt;p&gt;Another example of a plugin would be a frontend for debuggers. Allowing the user to set breakpoints, step through code, etc.&lt;/p&gt;&lt;p&gt;Aside from the ambitious scope of this plugin system I also want plugin support to be programming language agnostic. If you want to write a plugin in Java, go for it. Haskell? No problem. C? You got it boss!&lt;/p&gt;&lt;p&gt;This, of course, essentially means that plugins must be standalone executables or scripts. There’s no other way to make this happen unless you rebuild the editor. As a result, the plugins need to communicate with the editor somehow, and I think I’ll use the &lt;a href=&quot;https://www.jsonrpc.org/specification&quot; target=&quot;_blank&quot;&gt;json-rpc&lt;/a&gt; protocol to achieve this.&lt;/p&gt;&lt;h2&gt;Initial goals&lt;/h2&gt;&lt;p&gt;What I’ve outlined here is already pretty ambitious, and would take a long time to implement properly. Because of that I want to limit the scope to a sort of minimally usable editor for myself:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Syntax highlighting.&lt;/li&gt;&lt;li&gt;Support for &lt;a href=&quot;https://macromates.com/&quot; target=&quot;_blank&quot;&gt;TextMate&lt;/a&gt;/&lt;a href=&quot;https://code.visualstudio.com/&quot; target=&quot;_blank&quot;&gt;VSCode&lt;/a&gt; color themes.&lt;/li&gt;&lt;li&gt;Auto-completion via the &lt;a href=&quot;https://microsoft.github.io/language-server-protocol/&quot; target=&quot;_blank&quot;&gt;language server protocol&lt;/a&gt; (LSP).&lt;/li&gt;&lt;li&gt;Project overview bar.&lt;/li&gt;&lt;li&gt;Support for basic plugins&lt;ul&gt;&lt;li&gt;The benchmark here is to support plugins that change the input handling.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;I haven’t yet decided what will be part of the core editor and what will be implemented through plugins. Especially the syntax highlighting and LSP support. I guess that’s something I’ll need to find out along the way!&lt;/p&gt;&lt;h2&gt;What’s happening with this now?&lt;/h2&gt;&lt;p&gt;Despite the fact that &lt;a href=&quot;https://www.electronjs.org/&quot; target=&quot;_blank&quot;&gt;Electron&lt;/a&gt; apps &lt;em&gt;work fine&lt;/em&gt; most of the time, I dislike the idea of Electron when it’s not required (hint: it almost never is). As such I chose to find a suitable GUI framework that’s closer to being native, and that doesn’t leave me with many options.&lt;/p&gt;&lt;p&gt;I chose to go with C++ and &lt;a href=&quot;https://www.qt.io/&quot; target=&quot;_blank&quot;&gt;Qt&lt;/a&gt;. The Qt framework is mature, looks nice on all platforms, comes with a lot of good stuff, and I’ve worked with Qt before.&lt;/p&gt;&lt;p&gt;At the time of writing I have a working proof of concept available built with &lt;a href=&quot;https://riverbankcomputing.com/software/qscintilla/intro&quot; target=&quot;_blank&quot;&gt;QScintilla&lt;/a&gt; as the editor widget. Input handling is managed &lt;a href=&quot;https://git.sr.ht/~reykjalin/qode-kakoune&quot; target=&quot;_blank&quot;&gt;with a plugin&lt;/a&gt;, and &lt;a href=&quot;https://git.sr.ht/~reykjalin/qode-lexer&quot; target=&quot;_blank&quot;&gt;so is syntax highlighting and TextMate theme support&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://reykjalin.org/blog/new-code-editor/qode.webp&quot; alt=&quot;Screenshot of TypeScript code being edited with Qode&quot;&gt;&lt;/p&gt;&lt;p&gt;Right now I’m looking into whether I should continue to use QScintilla, or whether I should use &lt;a href=&quot;https://www.scintilla.org/index.html&quot; target=&quot;_blank&quot;&gt;Scintilla&lt;/a&gt; or &lt;a href=&quot;https://api.kde.org/frameworks/ktexteditor/html/&quot; target=&quot;_blank&quot;&gt;KTextEditor&lt;/a&gt;. My main gripe with QScintilla is that it’s based on a relatively outdated version of Scintilla. QScintilla is based on Scintilla v3.10.1, which was released in &lt;a href=&quot;https://sourceforge.net/projects/scintilla/files/scintilla/3.10.1/&quot; target=&quot;_blank&quot;&gt;October 2018&lt;/a&gt;. That version of Scintilla is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Long-term_support&quot; target=&quot;_blank&quot;&gt;LTS&lt;/a&gt; version, but it’s still several LTS versions behind, the latest one being &lt;a href=&quot;https://sourceforge.net/projects/scintilla/files/scintilla/3.11.2/&quot; target=&quot;_blank&quot;&gt;3.11.2&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I’m leaning towards using KTextEditor since that gives me a bunch of nice things out of the box, but that will really only work if there’s an easy way to include that in the project.&lt;/p&gt;&lt;p&gt;Once I’ve explored what it’s like to use some of the different editor widgets, and made up my mind as to which one I’ll use, I’ll work on LSP support and re-evaluate how far I want to take this after that.&lt;/p&gt;&lt;p&gt;Here’s to hoping this project isn’t &lt;strong&gt;way too ambitious&lt;/strong&gt;.&lt;/p&gt;</description>
        <link>https://reykjalin.org/blog/new-code-editor/</link>
        <pubDate>Thu, 28 May 2020 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/new-code-editor/</guid>
      </item>
    
      <item>
        <title>How I protect my privacy online</title>
        <description>&lt;p&gt;This post is in no way meant to be a guide or tutorial on how to protect your privacy on the internet, and neither is it a guide to staying completely anonymous online. However, this could be a good starting point for others interested in online privacy, so I wanted to share my general approach to online privacy.&lt;/p&gt;&lt;p&gt;I don’t have a strict need to hide what I’m doing online, but generally I want companies to know as little about me as possible. It’s not the end of the world if they do have &lt;em&gt;some&lt;/em&gt; information about me, so long as they can’t &lt;a href=&quot;https://www.avg.com/en/signal/facebook-listening-private-conversations&quot; target=&quot;_blank&quot;&gt;predict my wants&lt;/a&gt; and needs &lt;a href=&quot;https://www.wired.com/story/facebooks-listening-smartphone-microphone/&quot; target=&quot;_blank&quot;&gt;before I even realize&lt;/a&gt; they’re something I want or need.&lt;/p&gt;&lt;p&gt;This means that—as much as possible—I don’t want to be served ads, and I don’t want companies to track what I’m doing online. Google and Facebook run some of the &lt;a href=&quot;https://twitter.com/dhh/status/1205582897593430017&quot; target=&quot;_blank&quot;&gt;biggest ad networks&lt;/a&gt; on the internet, &lt;strong&gt;and&lt;/strong&gt; have a horrible history of &lt;a href=&quot;https://en.wikipedia.org/wiki/Cambridge_Analytica&quot; target=&quot;_blank&quot;&gt;literally selling your data&lt;/a&gt; to anyone that’s willing to pay for it. As a result they are the top 2 companies I want to avoid.&lt;/p&gt;&lt;h2&gt;No more Google&lt;/h2&gt;&lt;p&gt;I try my best to not use any of Google’s services. This means no Google search, no GMail, no Google Drive, no Google Docs, etc. There are plenty of alternatives out there, and I’d like to highlight a few here.&lt;/p&gt;&lt;h3&gt;So how do I find stuff online?&lt;/h3&gt;&lt;p&gt;I’ve been using &lt;a href=&quot;https://duckduckgo.com&quot; target=&quot;_blank&quot;&gt;DuckDuckGo&lt;/a&gt; for a while now with great success. There are still some cases where Google’s results are miles ahead of DDG, but I can use DDG for the vast majority of my online search needs.&lt;/p&gt;&lt;div class=&quot;block info notice&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;block title&quot;&gt;&lt;p&gt; February 2024 update&lt;/p&gt;&lt;/div&gt;&lt;p&gt;I’m currently using &lt;a href=&quot;https://kagi.com&quot;&gt;Kagi&lt;/a&gt; and really like it, but DDG is a much stronger general recommendation.&lt;/p&gt;&lt;/div&gt;&lt;div class=&quot;block info notice&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;block title&quot;&gt;&lt;p&gt; April 2025 update&lt;/p&gt;&lt;/div&gt;&lt;p&gt;I was subscribed to &lt;a href=&quot;https://kagi.com&quot;&gt;Kagi&lt;/a&gt; for a long time and would recommend it easily. However, I’ve had to reduce the subscriptions I’m paying for in my life, and Kagi was one of the first to go. DuckDuckGo is &lt;em&gt;good enough&lt;/em&gt; for the most part, although I find myself resorting to Google more and more. I also try to use &lt;a href=&quot;https://search.marginalia.nu&quot;&gt;Marginalia Search&lt;/a&gt;, an independent search engine for the small web, and like it for what it is. It does not, however, replace a general purpose search engine like DDG or Google.&lt;/p&gt;&lt;/div&gt;&lt;h3&gt;But what about e-mail?&lt;/h3&gt;&lt;p&gt;GMail serves you ads &lt;strong&gt;directly to your inbox&lt;/strong&gt; in their mobile app. That’s such a huge violation of what an e-mail provider should do that I have no words to express my disgust. It’s &lt;em&gt;abhorrent&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;I’ve stopped using my GMail account in favor of a more private e-mail provider: &lt;a href=&quot;https://mailbox.org&quot; target=&quot;_blank&quot;&gt;Mailbox.org&lt;/a&gt;. I then use &lt;a href=&quot;https://aerc-mail.org/&quot; target=&quot;_blank&quot;&gt;aerc&lt;/a&gt; to read my e-mails on my computer, and the Apple Mail client on my phone.&lt;/p&gt;&lt;p&gt;What Mailbox.org has over many other e-mail providers is that they support encryption out of the box in their web UI, and that’s why I initially chose them as my provider.&lt;/p&gt;&lt;p&gt;I’ve since realized encryption in the web UI isn’t something I really need. This setup is good for day to day use, but I find the Mailbox.org web UI to be very frustrating to use when I occasionally need to use it. As a result I’ve been looking for a simpler setup. Currently I’m eyeing both &lt;a href=&quot;https://fastmail.com&quot; target=&quot;_blank&quot;&gt;Fastmail&lt;/a&gt; and &lt;a href=&quot;https://www.migadu.com/&quot; target=&quot;_blank&quot;&gt;Migadu&lt;/a&gt;, both of which seem to have this figured out.&lt;/p&gt;&lt;p&gt;I also have a &lt;a href=&quot;https://protonmail.com&quot; target=&quot;_blank&quot;&gt;ProtonMail&lt;/a&gt; account that I don’t really use, but I’ve considered using them as well. Their service is solid but what’s holding me back is how difficult it is to &lt;em&gt;not&lt;/em&gt; use their web UI. Due to how they encrypt their e-mails you need to run a software bridge on your computer so your e-mail client can talk to ProtonMail.&lt;/p&gt;&lt;div class=&quot;block info notice&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;block title&quot;&gt;&lt;p&gt; February 2024 update&lt;/p&gt;&lt;/div&gt;&lt;p&gt;I eventually decided to use Migadu for myself and Fastmail for my wife. I can wholeheartedly recommend both, although Fastmail is expensive and Migadu is aimed at a more technical customer than the general public. A better general recommendation might be something like &lt;a href=&quot;https://zoho.com./&quot;&gt;Zoho&lt;/a&gt;, which I’ve heard decent things about, or &lt;a href=&quot;https://purelymail.com/&quot;&gt;Purelymail&lt;/a&gt;, which I’ve only heard good things about.&lt;/p&gt;&lt;p&gt;If I were setting up an email account for myself today I’d probably use Purelymail, but currently I like the control Migadu offers over how addresses on my domain work. I don’t know if Purelymail offers similar levels of control and simply don’t have a reason to check.&lt;/p&gt;&lt;p&gt;I don’t think I’ve ever used my ProtonMail account since writing this post, but maintain it anyway as an additional email account in case I need it, e.g. for recovery purposes.&lt;/p&gt;&lt;/div&gt;&lt;div class=&quot;block info notice&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;block title&quot;&gt;&lt;p&gt; April 2025 update&lt;/p&gt;&lt;/div&gt;&lt;p&gt;I recently had to reduce the cost of subscriptions we were paying for so I’ve moved all our emails to &lt;a href=&quot;https://purelymail.com&quot;&gt;Purelymail&lt;/a&gt;. It’s US$10 per year, which is significantly lower than even Migadu. Purelymail has fewer features than Migadu offers, but I find that it’s more than enough for my purposes.&lt;/p&gt;&lt;/div&gt;&lt;h3&gt;And how do you even access the internet?&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;https://www.google.com/chrome/&quot; target=&quot;_blank&quot;&gt;Google Chrome&lt;/a&gt; is an amazing web browser and especially so in terms of its developer tooling. However, &lt;a href=&quot;https://www.scss.tcd.ie/Doug.Leith/pubs/browser_privacy.pdf&quot; target=&quot;_blank&quot;&gt;the tracking&lt;/a&gt; it forces on you ruins all of that. It constantly rings up Google’s servers, automatically logs you into your Google account, and tracks your browsing history. Google will then use that data to serve you targeted ads everywhere they can. As much as Chrome impresses me on the technical side, the tracking inherent in the browser ruins it completely.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://mozilla.org/firefox&quot; target=&quot;_blank&quot;&gt;Mozilla Firefox&lt;/a&gt; has been my browser of choice for ages—even before I started thinking more about online privacy. It’s the browser I recommend to most people, and I don’t think that will change anytime soon. However, recently I’ve come to realize that Chrome works far better for my browsing experience, especially while working.&lt;/p&gt;&lt;p&gt;As a result I’ve been using the &lt;a href=&quot;https://brave.com&quot; target=&quot;_blank&quot;&gt;Brave&lt;/a&gt; browser. It comes with &lt;a href=&quot;https://www.scss.tcd.ie/Doug.Leith/pubs/browser_privacy.pdf&quot; target=&quot;_blank&quot;&gt;really good privacy settings out of the box&lt;/a&gt;, and is based on &lt;a href=&quot;https://en.wikipedia.org/wiki/Chromium_(web_browser)&quot; target=&quot;_blank&quot;&gt;Chromium&lt;/a&gt;. That’s the same browser Google Chrome is based on, so they work mostly the same. Brave comes with integrated ad-blocking and has &lt;a href=&quot;https://www.eff.org/https-everywhere&quot; target=&quot;_blank&quot;&gt;HTTPS Everywhere&lt;/a&gt; enabled by default, so there’s no need to install extensions to get that functionality.&lt;/p&gt;&lt;p&gt;On top of using browser features to reduce tracking, I also use &lt;a href=&quot;https://adguard.com/en/adguard-dns/overview.html&quot; target=&quot;_blank&quot;&gt;AdGuard&lt;/a&gt; as my DNS provider. AdGuard’s DNS blocks ads before they ever get to my browser, so my browser can focus on cleaning up what slips past AdGuard.&lt;/p&gt;&lt;p&gt;I also highly recommend &lt;a href=&quot;https://github.com/Eloston/ungoogled-chromium&quot; target=&quot;_blank&quot;&gt;Ungoogled Chromium&lt;/a&gt;. It’s a custom built Chromium that blocks the tracking introduced by Google in both Chromium and Google Chrome.&lt;/p&gt;&lt;p&gt;When I use Firefox and Ungoogled Chromium I use several extensions to block trackers: &lt;a href=&quot;https://github.com/gorhill/uBlock&quot; target=&quot;_blank&quot;&gt;uBlock Origin&lt;/a&gt;, &lt;a href=&quot;https://decentraleyes.org/&quot; target=&quot;_blank&quot;&gt;Decentraleyes&lt;/a&gt;, and &lt;a href=&quot;https://www.eff.org/https-everywhere&quot; target=&quot;_blank&quot;&gt;HTTPS Everywhere&lt;/a&gt;.&lt;/p&gt;&lt;div class=&quot;block info notice&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;block title&quot;&gt;&lt;p&gt; February 2024 update&lt;/p&gt;&lt;/div&gt;&lt;p&gt;I think I will forever be jumping between browsers. I currently use &lt;a href=&quot;https://arc.net/&quot;&gt;Arc&lt;/a&gt; for the user experience it offers. I have a general preference for browsers that don’t use Chromium because we desperately need more browser engine diversity on the Internet, but unfortunately I haven’t found a browser that works as well as Arc for my purposes.&lt;/p&gt;&lt;p&gt;I did briefly use &lt;a href=&quot;https://kagi.com/orion/&quot;&gt;Orion&lt;/a&gt;, which is based on WebKit (the engine Safari uses), and it’s the browser that has come closest to Arc for me, but unfortunately just didn’t work as well as Arc does. The UX of Arc is just that good.&lt;/p&gt;&lt;p&gt;Firefox is still the best general recommendation for a “classic” web browsing experience.&lt;/p&gt;&lt;/div&gt;&lt;div class=&quot;block info notice&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;block title&quot;&gt;&lt;p&gt; April 2025 update&lt;/p&gt;&lt;/div&gt;&lt;p&gt;Like I predicted, Arc didn’t stick. I’m now using &lt;a href=&quot;https://zen-browser.app/&quot;&gt;Zen&lt;/a&gt;, a Firefox-based browser that has learned a lot from how Arc’s UI works and applied it to Firefox. I find that Chrome is generally better supported, so some services I still have to open in Chrome, but for the most part Zen (and Firefox) just work!&lt;/p&gt;&lt;p&gt;These days I’d easily recommend Zen over every other browser out there.&lt;/p&gt;&lt;/div&gt;&lt;h2&gt;No more Facebook&lt;/h2&gt;&lt;p&gt;This one has been one of the hardest things to move away from, not because I use Facebook a lot, but because where I’m from the community runs on Facebook. If you’re not on Facebook you don’t get invites to events, and people have a hard time reaching you (maybe they’ve never heard about mobile phones?). As a result you generally just miss out on what’s happening around you.&lt;/p&gt;&lt;p&gt;As much as that frustrates me, I don’t really mind missing out on most things in my community; I hear about events through my friends and family anyway. What &lt;strong&gt;does&lt;/strong&gt; frustrate me however, is that I can’t get away from Facebook Messenger. If I where to delete my Facebook account, most of my family and friends wouldn’t be able to reach me.&lt;/p&gt;&lt;p&gt;I don’t live in my home country anymore, and haven’t for the past 3+ years at the time of writing. That means family and friends can’t call or text me by phone, leaving online communication as the only viable option. Since everyone is already on Facebook everyone uses Facebook Messenger to communicate. I really don’t want to force people to install an app just to talk to me, although I made an exception to that rule for my parents and close friends 🙂.&lt;/p&gt;&lt;p&gt;The vast majority of online conversations I have are with my girlfriend, parents, and a couple of close friends. Thankfully they’ve all been willing to switch to either &lt;a href=&quot;https://telegram.org/&quot; target=&quot;_blank&quot;&gt;Telegram&lt;/a&gt; or &lt;a href=&quot;https://www.signal.org/&quot; target=&quot;_blank&quot;&gt;Signal&lt;/a&gt;. While I’d love to use Signal for all my communications, Telegram’s user experience is &lt;strong&gt;far better&lt;/strong&gt; in every way. Telegram’s &lt;a href=&quot;https://telegram.org/faq#q-why-not-just-make-all-chats-secret&quot; target=&quot;_blank&quot;&gt;default encryption model&lt;/a&gt; is good enough for me for most communications; if I need proper end to end encryption I can use &lt;a href=&quot;https://telegram.org/faq#q-how-are-secret-chats-different&quot; target=&quot;_blank&quot;&gt;secret chats&lt;/a&gt; or Signal.&lt;/p&gt;&lt;h2&gt;No more social media&lt;/h2&gt;&lt;p&gt;Well, almost.&lt;/p&gt;&lt;p&gt;I’ve removed Facebook, Instagram, and Twitter from my phone, and rarely visit any of them. I might check each service once or twice every 2 months. Instead I’ve made a &lt;a href=&quot;https://en.wikipedia.org/wiki/Mastodon_(software)&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt; account on the &lt;a href=&quot;https://fosstodon.org/about&quot; target=&quot;_blank&quot;&gt;Fosstodon&lt;/a&gt; instance, and I can honestly say it’s been great! Most of the discussions happening there are relevant to my interests, and if I want to follow specific people from other instances that’s super easy to do.&lt;/p&gt;&lt;p&gt;I do miss following some personalities on Twitter, but not so much that I go back to check on their updates. Honestly, I think I’m much more productive since I’m not looking at those updates 😅.&lt;/p&gt;&lt;div class=&quot;block info notice&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;block title&quot;&gt;&lt;p&gt; April 2025 update&lt;/p&gt;&lt;/div&gt;&lt;p&gt;I’ve moved my Mastodon account to &lt;a href=&quot;https://social.treehouse.systems/@reykjalin&quot;&gt;Treehouse Systems&lt;/a&gt;. One more benefit of Mastodon’s decentralization is that you can easily move your account to a new host network if you want!&lt;/p&gt;&lt;/div&gt;&lt;h2&gt;No more Dropbox&lt;/h2&gt;&lt;p&gt;While &lt;a href=&quot;https://www.dropbox.com/&quot; target=&quot;_blank&quot;&gt;Dropbox&lt;/a&gt;‘s policies aren’t exactly bad, they’re &lt;a href=&quot;https://tosdr.org/#dropbox&quot; target=&quot;_blank&quot;&gt;not that great&lt;/a&gt; either. My biggest issue is that data is not encrypted &lt;em&gt;before&lt;/em&gt; it’s sent to Dropbox. That means that even though Dropbox encrypts the data on their side &lt;em&gt;they still have the potential to read and process all of it&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;I’ve already talked about why I don’t like Google products so I don’t think I need to reiterate why I don’t want to use Google Drive.&lt;/p&gt;&lt;p&gt;As a result I’ve been using &lt;a href=&quot;https://tresorit.com/&quot; target=&quot;_blank&quot;&gt;Tresorit&lt;/a&gt; for a while with great success, although their mobile app experience is lacking. Right now I’m looking at buying a subscription to &lt;a href=&quot;https://www.sync.com/&quot; target=&quot;_blank&quot;&gt;Sync.com&lt;/a&gt; or paying for a &lt;a href=&quot;https://en.wikipedia.org/wiki/Virtual_private_server&quot; target=&quot;_blank&quot;&gt;VPS&lt;/a&gt; with a good amount of storage and self-hosting &lt;a href=&quot;https://nextcloud.com/&quot; target=&quot;_blank&quot;&gt;Nextcloud&lt;/a&gt; instead.&lt;/p&gt;&lt;div class=&quot;block info notice&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;block title&quot;&gt;&lt;p&gt; February 2024 update&lt;/p&gt;&lt;/div&gt;&lt;p&gt;I currently use iCloud. I find that I trust Apple &lt;em&gt;enough&lt;/em&gt; to hold my data for me. I’m also deeply embedded in Apple’s ecosystem where my computer of choice is a Mac and my phone of choice is an iPhone. iCloud simply works best for those devices and the integration is seamless.&lt;/p&gt;&lt;p&gt;For a general recommendation today I’d probably just recommend buying access to a Nextcloud instance or hosting one yourself.&lt;/p&gt;&lt;/div&gt;&lt;h2&gt;In conclusion&lt;/h2&gt;&lt;p&gt;My general approach here is to try to use services that encrypt my communication with them, and ideally store my data in such a way that they can’t access it, e.g. services like Tresorit and Signal. I also try to limit my exposure to services that are known bad actors when it comes to tracking and advertising.&lt;/p&gt;&lt;p&gt;I could take this idea much further; block scripts from executing in my web browser, exclusively browse the web through the &lt;a href=&quot;https://www.torproject.org/&quot; target=&quot;_blank&quot;&gt;Tor browser&lt;/a&gt;, never sign up to services that require any sort of personal information, and so on and so forth, but to that’s too extreme for me. If I did, there would be multiple services I simply wouldn’t be able to use.&lt;/p&gt;&lt;p&gt;That said I don’t think it’s unreasonable for services to ask for some identifiable information to prevent spam, although it’s certainly not the best solution. So long as the service provider doesn’t gather too much information—and so long as they don’t abuse that information—I don’t mind giving up some information about me in exchange for their services.&lt;/p&gt;</description>
        <link>https://reykjalin.org/blog/online-privacy/</link>
        <pubDate>Mon, 20 Apr 2020 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/online-privacy/</guid>
      </item>
    
      <item>
        <title>Calling Fable from TypeScript</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://fable.io/&quot; target=&quot;_blank&quot;&gt;Fable&lt;/a&gt; is a framework used to compile F# code to JavaScript. I’ve been using it in a &lt;a href=&quot;https://www.typescriptlang.org/&quot; target=&quot;_blank&quot;&gt;TypeScript&lt;/a&gt; project — actually going so far as using F# for most of the code.&lt;/p&gt;&lt;p&gt;Fable has a really nice interface that allows easy interoperability with JavaScript, although it can take some time to fully grasp how to use the interface.&lt;/p&gt;&lt;p&gt;Unfortunately this interface &lt;a href=&quot;https://fable.io/docs/communicate/fable-from-js.html&quot; target=&quot;_blank&quot;&gt;isn’t really well documented&lt;/a&gt;. They do have some introductory instructions there, but nothing really comprehensive.&lt;/p&gt;&lt;p&gt;While using Fable in my project I had a really hard time figuring out how to call the compiled F# code from my TypeScript project, even going so far as converting the project to JavaScript while I was figuring this out because I &lt;em&gt;really&lt;/em&gt; wanted to use F# instead of TypeScript.&lt;/p&gt;&lt;p&gt;So, without further ado, let me explain &lt;em&gt;how&lt;/em&gt; you go about importing your compiled F# code into your TypeScript project!&lt;/p&gt;&lt;h2&gt;My interpretation of the documentation&lt;/h2&gt;&lt;p&gt;After reading the documentation I thought I should import the Fable code into &lt;code&gt;index.ts&lt;/code&gt; with an import command like &lt;code&gt;import { helloWorld } from &amp;apos;fsharp/helloWorld.fs&amp;apos;&lt;/code&gt; and a corresponding &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html&quot; target=&quot;_blank&quot;&gt;declaration file&lt;/a&gt;, but that doesn’t really work; you can’t import F# code directly into TypeScript, or any non-TypeScript code for that matter.&lt;/p&gt;&lt;p&gt;To import non-TypeScript code into your TypeScript project, you need a declaration file, but before getting to that I want to talk about project folder structure. This is because the declaration file itself isn’t enough, it has to be &lt;strong&gt;in the right location&lt;/strong&gt;.&lt;/p&gt;&lt;h2&gt;Project folder structure&lt;/h2&gt;&lt;p&gt;Lets assume your project folder looks something like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;project/
|- src/
   |- index.ts
   |- fsharp/
      |- helloWorld.fs
      |- helloWorld.fsproj
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And let’s assume you’ve set up your compilers such that the compiled output looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;project/
|- out/
   |- index.js
   |- fsharp/
      |- helloWorld.js
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To be able to import your F# code into your TypeScript project, you need to think about the &lt;strong&gt;output&lt;/strong&gt; folder structure, and subsequently where you should put your declaration files.&lt;/p&gt;&lt;p&gt;You always need to think about the end result; the compiled code will always be pure JavaScript, and thus you need to think as if you’re working in JavaScript.&lt;/p&gt;&lt;p&gt;Assume for a moment that you’re the one writing the code in the compiled output &lt;code&gt;out/index.js&lt;/code&gt;. If you wanted to use the code from &lt;code&gt;out/fsharp/helloWorld.js&lt;/code&gt; you’d import it with &lt;code&gt;import { helloWorld } from &amp;apos;fsharp/helloWorld.js&amp;apos;&lt;/code&gt;. Because of this, we need the import in &lt;code&gt;src/index.ts&lt;/code&gt; to be that exact import.&lt;/p&gt;&lt;p&gt;The easiest way to achieve this is to have the source directory mirror the structure of the output directory. Take a look at the example I outlined above again, here, in its entirety:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;project/
|- out/
   |- index.js
   |- fsharp/
      |- helloWorld.js
|- src/
   |- index.ts
   |- fsharp/
      |- helloWorld.fs
      |- helloWorld.fsproj
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You’ll see that the &lt;code&gt;src/&lt;/code&gt; and &lt;code&gt;out/&lt;/code&gt; directories look the same, aside from the F# specific &lt;code&gt;helloWorld.fsproj&lt;/code&gt;.&lt;/p&gt;&lt;h2&gt;Where to put your declaration files&lt;/h2&gt;&lt;p&gt;With the mirrored folder structure in mind, we put the declaration file somewhere that will make the import from &lt;code&gt;src/index.ts&lt;/code&gt; work for the compiled version of the JavaScript. In this case that would be in &lt;code&gt;src/fsharp&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;We also need to make sure that the name of the declaration file fits the import. Remember that the compiled F# code will live in &lt;code&gt;out/fsharp/helloWorld.js&lt;/code&gt;. For this reason we’ll call the declaration file &lt;code&gt;helloWorld.js.d.ts&lt;/code&gt; and save it in &lt;code&gt;src/fsharp&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;project/
|- out/
   |- index.js
   |- fsharp/
      |- helloWorld.js
|- src/
   |- index.ts
   |- fsharp/
      |- helloWorld.js.d.ts
      |- helloWorld.fs
      |- helloWorld.fsproj
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can think of the declaration file name as &lt;code&gt;&amp;lt;import_path&amp;gt;.js.d.ts&lt;/code&gt;.&lt;/p&gt;&lt;h2&gt;What should you put in your declaration file&lt;/h2&gt;&lt;p&gt;This is another part where the documentation fails us. Lets assume your &lt;code&gt;src/fsharp/helloWorld.fs&lt;/code&gt; file looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;fsharp&quot;&gt;&lt;span class=&quot;comment_documentation comment spell&quot;&gt;// helloWorld.fs&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;module keyword_exception module_builtin type_builtin&quot;&gt;Hello&lt;/span&gt;
&lt;span class=&quot;keyword_function&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;keyword_exception module_builtin function&quot;&gt;helloWorld&lt;/span&gt; &lt;span class=&quot;punctuation_bracket variable_parameter&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword_exception module_builtin variable_parameter&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;keyword_exception module_builtin type_builtin type&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;keyword_exception module_builtin type_builtin type&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
	&lt;span class=&quot;keyword_exception module_builtin function_call&quot;&gt;sprintf&lt;/span&gt; &lt;span class=&quot;string constant variable spell&quot;&gt;&amp;quot;Hello, %s!&amp;quot;&lt;/span&gt; &lt;span class=&quot;keyword_exception module_builtin type_builtin variable&quot;&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Based on the documentation, the declaration file for this &lt;code&gt;helloWorld.fs&lt;/code&gt; should look something like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;ts&quot;&gt;/** helloWorld.js.d.ts */

&lt;span class=&quot;keyword&quot;&gt;declare&lt;/span&gt; module &amp;quot;helloWorld.js&amp;quot; {
	function &lt;span class=&quot;type&quot;&gt;helloWorld&lt;/span&gt;( &lt;span class=&quot;variable_parameter type&quot;&gt;name&lt;/span&gt;: &lt;span class=&quot;type_builtin&quot;&gt;string&lt;/span&gt; ): &lt;span class=&quot;type_builtin&quot;&gt;string&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, I found that this didn’t work for me &lt;strong&gt;at all&lt;/strong&gt;. Nothing. Nada. The TypeScript compiler simply told me that &lt;code&gt;helloWorld.js is not a module&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Instead, you’ll want your declaration file to look like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;ts&quot;&gt;/** helloWorld.js.d.ts */

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; function &lt;span class=&quot;type&quot;&gt;helloWorld&lt;/span&gt;( &lt;span class=&quot;variable_parameter type&quot;&gt;name&lt;/span&gt;: &lt;span class=&quot;type_builtin&quot;&gt;string&lt;/span&gt; );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple, and to the point. Just export your function signature, and be done with it.&lt;/p&gt;&lt;p&gt;If your code is a little more substantial, you have to change the declaration file accordingly:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;fsharp&quot;&gt;&lt;span class=&quot;comment_documentation comment spell&quot;&gt;// helloWorld.fs&lt;/span&gt;

&lt;span class=&quot;keyword_type&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;keyword_exception module_builtin type_definition&quot;&gt;HelloMessage&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;keyword_exception property module_builtin&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;keyword_exception module_builtin type_builtin type&quot;&gt;string&lt;/span&gt;
		&lt;span class=&quot;keyword_exception module_builtin type_builtin&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;keyword_exception module_builtin type_builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;keyword_exception module_builtin function&quot;&gt;helloWorld&lt;/span&gt; &lt;span class=&quot;punctuation_bracket variable_parameter&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword_exception module_builtin variable_parameter&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;keyword_exception module_builtin type_builtin type&quot;&gt;HelloMessage&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;keyword_exception module_builtin type_builtin type&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
	&lt;span class=&quot;keyword_exception module_builtin function_call&quot;&gt;sprintf&lt;/span&gt;
		&lt;span class=&quot;string constant variable spell&quot;&gt;&amp;quot;Hello %s! Your message is: %s&amp;quot;&lt;/span&gt;
		&lt;span class=&quot;keyword_exception module_builtin type_builtin variable variable_member&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter variable&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;keyword_exception module_builtin type_builtin variable&quot;&gt;name&lt;/span&gt;
		&lt;span class=&quot;keyword_exception module_builtin variable variable_member&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter variable&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;keyword_exception module_builtin type_builtin variable&quot;&gt;message&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;ts&quot;&gt;/** helloWorld.js.d.ts */

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; class &lt;span class=&quot;type&quot;&gt;HelloMessage&lt;/span&gt; {
	name: &lt;span class=&quot;type_builtin&quot;&gt;string&lt;/span&gt;;
	message: &lt;span class=&quot;type_builtin&quot;&gt;string&lt;/span&gt;;
}

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; function &lt;span class=&quot;type&quot;&gt;helloWorld&lt;/span&gt;( &lt;span class=&quot;variable_parameter type&quot;&gt;msg&lt;/span&gt;: &lt;span class=&quot;type&quot;&gt;HelloMessage&lt;/span&gt; ): &lt;span class=&quot;type_builtin&quot;&gt;string&lt;/span&gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;In conclusion&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;Mirror the compiled JavaScript folder hierarchy in your source folder.&lt;/li&gt;&lt;li&gt;Put your declaration files in the same folder as your F# source files.&lt;/li&gt;&lt;li&gt;Name your declaration files &lt;code&gt;&amp;lt;f#_source_name&amp;gt;.js.d.ts&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Import in TypeScript with &lt;code&gt;import { yourFunction } from &amp;apos;path/to/fsharp/source.js&amp;apos;&lt;/code&gt;.&lt;/li&gt;&lt;/ol&gt;</description>
        <link>https://reykjalin.org/blog/call-fable-from-ts/</link>
        <pubDate>Wed, 05 Feb 2020 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/call-fable-from-ts/</guid>
      </item>
    
      <item>
        <title>Why don&apos;t we have good code editors?</title>
        <description>&lt;p&gt;I’ve spent a long time trying different code editors, trying to find the perfect one for me. And yes, I’m well aware that finding the &lt;em&gt;perfect&lt;/em&gt; editor can certainly &lt;em&gt;sound&lt;/em&gt; impossible, but should it be in today’s software ecosystem? I don’t think so.&lt;/p&gt;&lt;p&gt;To be clear, this rant—let’s be honest, this post definitely &lt;em&gt;is&lt;/em&gt; a rant—does not propose any solutions for the issues I have with today’s code editors. Instead it seeks to point out what it might take to create a good code editor.&lt;/p&gt;&lt;p&gt;I’d also like to point out we have a lot of great text editors! Just not text editors that are also great &lt;em&gt;code&lt;/em&gt; editors.&lt;/p&gt;&lt;h2&gt;My frustrations with today’s code editors&lt;/h2&gt;&lt;p&gt;The most limiting factor for my choice of code editors is that I &lt;em&gt;need&lt;/em&gt; modal editing. After learning how to use &lt;a href=&quot;https://www.gnu.org/software/emacs/&quot; target=&quot;_blank&quot;&gt;Emacs&lt;/a&gt;, and then &lt;a href=&quot;https://www.vim.org/&quot; target=&quot;_blank&quot;&gt;Vim&lt;/a&gt;, I can’t imagine going back to the typical non-modal text editing. These editors make it much more comfortable to edit text (and code), and ruined every other way of editing for me.&lt;/p&gt;&lt;p&gt;Now when I try a new editor or IDE the first thing I do is find a Vim emulation extension. Usually, the extension can’t fully support that style of editing because the editor wasn’t built with that type of editing in mind. That lack of support for modal editing eventually makes me frustrated enough to go back to Vim.&lt;/p&gt;&lt;p&gt;I will then use Vim for a couple of days, trying to set up a good environment for what I need. That inevitably fails and I go back to using an editor like &lt;a href=&quot;https://code.visualstudio.com/&quot; target=&quot;_blank&quot;&gt;VSCode&lt;/a&gt;, that has a “good enough” Vim emulation plugin.&lt;/p&gt;&lt;p&gt;I’ve even gone so far as to try wrappers for Vim like &lt;a href=&quot;https://github.com/onivim/oni&quot; target=&quot;_blank&quot;&gt;Oni&lt;/a&gt; and &lt;a href=&quot;https://github.com/daa84/neovim-gtk&quot; target=&quot;_blank&quot;&gt;neovim-gtk&lt;/a&gt;, but there are always some issues. Inevitably there will be something that makes it so unappealing that I go back to something else like VSCode.&lt;/p&gt;&lt;p&gt;And I hate that. I don’t like using VSCode—I don’t hate it either—but I’m stuck with it because it’s the best editor available. I don’t like the Vim emulation extension, because it’s often slow, and it doesn’t perfectly replicate the utility of Vim. The best Vim emulation extension I’ve come across is – oddly enough – in &lt;a href=&quot;https://doc.qt.io/qtcreator/&quot; target=&quot;_blank&quot;&gt;QtCreator&lt;/a&gt;. If only I could use QtCreator for everything…&lt;/p&gt;&lt;h2&gt;What &lt;em&gt;I&lt;/em&gt; think good code editors should have&lt;/h2&gt;&lt;h3&gt;Modal editing first&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;Modal editing should be the first priority; “normal” editing should come after.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Me, myself, and I&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Modal editing makes repetitive tasks while editing code &lt;em&gt;so easy&lt;/em&gt; to perform. Now, you might be thinking “why would I want to repeat editing tasks while writing code?”. Well, you won’t want it for &lt;em&gt;writing&lt;/em&gt; code, you’ll want it for &lt;em&gt;editing&lt;/em&gt;. Deleting multiple lines, writing on multiple lines simultaneously, multiple selections, etc., etc.&lt;/p&gt;&lt;p&gt;Besides, “normal” editing is just a mode in modal editing. If you don’t want the modal editing enabled, just disable it. The editor already supports a normal editing mode—that’s how you write most of the time after all—and that should make this configuration trivial.&lt;/p&gt;&lt;p&gt;Editors that do this well are editors like Vim and &lt;a href=&quot;https://kakoune.org/&quot; target=&quot;_blank&quot;&gt;Kakoune&lt;/a&gt;, and—in my opinion—Emacs, but to a lesser extent.&lt;/p&gt;&lt;h3&gt;A modern, &lt;em&gt;graphical&lt;/em&gt; UI&lt;/h3&gt;&lt;p&gt;Terminal editors are out of date. By this I don’t mean that they’re obsolete; I mean their user experience is lagging far behind modern standards. Using editors like Vim and Emacs is not a good experience for the user. The user interface &lt;em&gt;sucks&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;I know there are some crazies out there that do everything in a terminal—I mean have you heard of the operating system called Emacs? It’s great, but despite being decades old, &lt;em&gt;still&lt;/em&gt; lacks a decent text editor. In all seriousness though, a terminal does not a pleasant user experience make.&lt;/p&gt;&lt;p&gt;You’ll see many Vim/Emacs users argue that you’re faster by getting used to working only with the keyboard to control the UI. Sure, you might gain a few milliseconds in specific cases, but that’s not how anyone works today. Using the mouse is comfortable, intuitive, and &lt;em&gt;easy&lt;/em&gt;. Using a terminal editor that doesn’t properly support doing everything with a mouse just doesn’t hold up in today’s environment of graphical software.&lt;/p&gt;&lt;p&gt;Of course, when a terminal is all you have, these editors are amazing. But that doesn’t change the fact that the UI is &lt;em&gt;horrible&lt;/em&gt;, and far from being modern.&lt;/p&gt;&lt;p&gt;Just look at editors like &lt;a href=&quot;https://atom.io/&quot; target=&quot;_blank&quot;&gt;Atom&lt;/a&gt;, &lt;a href=&quot;https://code.visualstudio.com/&quot; target=&quot;_blank&quot;&gt;VSCode&lt;/a&gt;, and &lt;a href=&quot;https://www.sublimetext.com/&quot; target=&quot;_blank&quot;&gt;Sublime Text&lt;/a&gt;. All of these editors come with excellent UIs that make editing multiple files—working on a &lt;em&gt;project&lt;/em&gt;, not just a file—an easier experience. The way these editors achieve this is through graphical representation of the project hierarchy—the file sidebar—and many other quality of life improvements—like being able to use your mouse!&lt;/p&gt;&lt;h3&gt;Support user made extensions&lt;/h3&gt;&lt;p&gt;Most text editors have this today, but making extension development easy, and making it easy for users to find and install extensions, is a tricky problem to solve. VSCode and Atom have clearly figured out a way that works for those editors. It’s easy to search for, and install, any extension you might want.&lt;/p&gt;&lt;p&gt;Relative to those editors, Vim and Emacs make this very difficult. You need to either install a package manager, and then the extension, or install every extension manually. The process is a pain, even when you get it right.&lt;/p&gt;&lt;p&gt;When it comes to extensions the following tasks &lt;em&gt;must&lt;/em&gt; be easy:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Searching for extensions&lt;/li&gt;&lt;li&gt;Installing extensions&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;An easy way to search for extensions is important because easy discoverability is the key to getting users to use extensions. Easy installation—as in no more than 1 click after finding the extension—is equally important so users will not only search for extensions, but actually &lt;em&gt;use&lt;/em&gt; them.&lt;/p&gt;&lt;p&gt;It is, of course, important to design the text editor with all this in mind, and some way to allow extensions to interact with the editor.&lt;/p&gt;&lt;h3&gt;Smart auto-complete&lt;/h3&gt;&lt;p&gt;This is simple. I want &lt;a href=&quot;https://code.visualstudio.com/docs/editor/intellisense&quot; target=&quot;_blank&quot;&gt;IntelliSense&lt;/a&gt; everywhere. &lt;em&gt;Built in&lt;/em&gt;. Is that so much to ask?&lt;/p&gt;&lt;h3&gt;Peek at implementation/definition&lt;/h3&gt;&lt;p&gt;Being able to peek at a function’s implementation makes it so much easier to work with code! You don’t have to remove yourself from the current context to find another file, locate the function, see what it does, navigate back to where you where, and get back to work. &lt;a href=&quot;https://www.jetbrains.com/idea/&quot; target=&quot;_blank&quot;&gt;IntelliJ&lt;/a&gt; really has this one figured out.&lt;/p&gt;&lt;h3&gt;Fast and responsive&lt;/h3&gt;&lt;p&gt;I don’t mind &lt;a href=&quot;https://electronjs.org/&quot; target=&quot;_blank&quot;&gt;Electron&lt;/a&gt; apps, when they work properly and feel responsive. I happily use programs like &lt;a href=&quot;https://simplenote.com/&quot; target=&quot;_blank&quot;&gt;Simplenote&lt;/a&gt;, &lt;a href=&quot;https://tutanota.com/&quot; target=&quot;_blank&quot;&gt;Tutanota&lt;/a&gt;, and &lt;a href=&quot;https://signal.org&quot; target=&quot;_blank&quot;&gt;Signal&lt;/a&gt; all the time, because they don’t feel sluggish and unresponsive.&lt;/p&gt;&lt;p&gt;Programs like &lt;a href=&quot;https://www.spotify.com/us/download/linux/&quot; target=&quot;_blank&quot;&gt;Spotify&lt;/a&gt;, Atom, and VSCode however, &lt;em&gt;do&lt;/em&gt; feel sluggish and unresponsive at times. It takes a while to load large files. Scrolling halts. Loading the syntax highlighting takes a while. It just feels &lt;em&gt;slow&lt;/em&gt;. Not to mention they also use a lot of memory and system resources, even more so than your average Electron app. If they have access to all of that memory and computing power, surely they should be able to run without lagging right?&lt;/p&gt;&lt;h3&gt;All of these should come out of the box&lt;/h3&gt;&lt;p&gt;I’m sure I could spend days setting up the perfect Vim environment, but my question is: Why should I have to do that? Having all of these come out of the box shouldn’t be that hard in 2019. Some editors have perfect implementations of at least one of the features I want, so an editor that has all of these is clearly not an impossible feat. Why has no one made one yet?&lt;/p&gt;&lt;h2&gt;There is hope&lt;/h2&gt;&lt;p&gt;I’m actually very excited to see where new editors like &lt;a href=&quot;https://github.com/xi-editor/xi-editor&quot; target=&quot;_blank&quot;&gt;Xi&lt;/a&gt; and &lt;a href=&quot;https://v2.onivim.io/&quot; target=&quot;_blank&quot;&gt;Oni2&lt;/a&gt; will end up. Specifically I’m actively following the discussions surrounding new frontends for Xi, as well as their &lt;a href=&quot;https://github.com/xi-editor/xi-editor/issues/302&quot; target=&quot;_blank&quot;&gt;open issue on modal editing&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I’ve even thought about just doing this myself, but so far I haven’t found the motivation and time to actively work on a big undertaking like creating a text editor I like.&lt;/p&gt;&lt;p&gt;I can only hope someone will see the light and save us all sometime soon.&lt;/p&gt;</description>
        <link>https://reykjalin.org/blog/no-good-code-editor/</link>
        <pubDate>Fri, 27 Sep 2019 00:00:00 +0000</pubDate>
        <guid>https://reykjalin.org/blog/no-good-code-editor/</guid>
      </item>
    
  </channel>
</rss>
