{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Notes to self",
  "home_page_url": "https://hjort.land",
  "feed_url": "https://hjort.land/feed.json",
  "description": "Hi, I'm Andreas Hjortland, I write this blog and work as a software developer for ACOS AS\n",
  "items": [
    {
      "id": "https://hjort.land/blog/2026/05/31/redesign.html",
      "url": "https://hjort.land/blog/2026/05/31/redesign.html",
      "title": "Redesigning my blog",
      "content_html": "<p>I am in the process of migrating my homelab to nomad and came over some of my older iterations of my web sites, all the\nway back to when I was 15 years old and just discovered that I could write text in notepad and get it to appear on the\ninternet, so in homage to that I wanted to go a trip down memory lane and perhaps take these designs into the future.</p>\n\n<h2 id=\"hello-world\">Hello, world!</h2>\n\n<p><img src=\"/assets/2026-05-31-redesign/first-page.avif\" alt=\"Screenshot of my first web page\" /></p>\n\n<p>This was my first web page I ever “published”. It was part of a school project where we should present Norwegian\nconsumer rights, and since I was a young kid that rather wanted to play with my computer than do the project, I\nvolunteered to make a web site instead of a power point presentation. I made the background gif by myself using\nMicrosoft Paint and I had just discovered other browsers than Internet Explorer and gotten a shiny new LCD monitor so I\ndecided that I would target Opera and a resolution of 1280x1024. When I try opening it up today I get a whole host of\nencoding issues since this was before I knew what text-encoding was and I don’t want to change it now. The page is a\npristine showcase of table layout and I have so many implicit assumptions about the browser and resolution, it really\nhaven’t stood up to the test of time, but I still can’t help but smile when looking back at where I (and the web) came\nfrom.</p>\n\n<h2 id=\"stage-2\">Stage 2</h2>\n\n<p><img src=\"/assets/2026-05-31-redesign/second-page.avif\" alt=\"Screenshot of my second web page\" /></p>\n\n<p>After the school course I was inspired by actually being able to build my own web page, so I decided to try my hands on\nthe newfangled weblog thingy that people were talking about. I installed linux on a spare Pentium 3 machine I had and\ninstalled the <abbr title=\"Linux, Apache, MySql and PHP\">LAMP</abbr> stack and Wordpress. I found this theme online and\nthought that was cool, so I just stole the design. I don’t remember if this was an actual wordpress theme that I\ninstalled or I just looked at the source code and replicated it. When I did some research, I actually found the theme\nstill online <a href=\"https://www.oswd.org/user/profile/id/17887/\">here</a>, so take a look if you want to.</p>\n\n<p>In my humble opinion this era of web design is the best, but I am pretty sure this is just nostalgia talking =)</p>\n\n<h2 id=\"university-era\">University era</h2>\n\n<p><img src=\"/assets/2026-05-31-redesign/third-page.avif\" alt=\"Screenshot of a page I made during my first year of university\" /></p>\n\n<p>This is another course work page, but now I had moved to first year of university. Unfortunately I could only find this\nbase template and not the actual content, but I remember being quite happy with myself that I was able to set classes on\nthe active page and highlight the current page using PHP. Even though I have been interested in computers since the\nmid-1990s, I was more into using them than programming them before I started my degree so I was quite new to the whole\n“programming” business until then.</p>\n\n<p>This was actually my last personal web page / blog before I revisited it during covid and updated it to the current(?)\ntheme.</p>\n\n<h2 id=\"today\">Today</h2>\n\n<picture>\n    <source srcset=\"/assets/2026-05-31-redesign/fourth-page-light.avif\" media=\"(prefers-color-scheme: dark)\" />\n    <img alt=\"My current blog (at time of writing)\" src=\"/assets/2026-05-31-redesign/fourth-page-dark.avif\" />\n</picture>\n\n<p>The theme I am using today was a covid project where I decided it was time to update my blog after listening to Troy\nHunt talk about the importance of having a persona and blog online for building my career though I haven’t been\nespecially active, so I can’t say that I managed to make anything out of it beyond the enjoyment  building stuff. In\n2026 I added support for dark mode, but otherwise the site has been quite static since I did that. If you toggle between\nlight and dark mode, you can actually see that the screenshot above will toggle to the opposite of what mode the page is\nin otherwise.</p>\n\n<hr />\n\n<p>Thank you for taking the time to read my nostalgic rambling. I have actually added a small easter egg on the page where\nI have reimplemented these themes with my current layout. Note that these themes are quite hacky with absolute\npositioning <code class=\"language-plaintext highlighter-rouge\">display: contents;</code>, hard coded paddings and more to make my blog look the way the old themes looked\nwithout changing any of my markup. This means that it will probably not be accessible at all and you will notice that a\nlot of text is unselectable because I have been using extensively pseudo-elements with <code class=\"language-plaintext highlighter-rouge\">content: '...';</code> blocks to\ninject content that just isn’t in the DOM.</p>\n\n<p>You can use this select box <select class=\"theme-selector\" aria-label=\"Select theme\" name=\"theme\">\n\t<option selected=\"\">2020</option>\n\t<option>2011</option>\n\t<option>2008</option>\n\t<option>2005</option>\n</select>\n to change the layout dynamically. It is also available in\nthe main toolbar if you hover over it or tab to it just to the left of the RSS icon.</p>\n",
      "date_published": "2026-05-31T00:00:00+00:00"
    },
    {
      "id": "https://hjort.land/ngxs/2022/06/04/ngxs-message-plugin.html",
      "url": "https://hjort.land/ngxs/2022/06/04/ngxs-message-plugin.html",
      "title": "Introducing ngxs message plugin",
      "content_html": "<p>TLDR; check out the project on <a href=\"https://github.com/Andreas-Hjortland/ngxs-message-plugin/\" target=\"external\">github</a>\nor install it using <code class=\"language-plaintext highlighter-rouge\">npm i ngxs-message-plugin</code> / <code class=\"language-plaintext highlighter-rouge\">yarn add ngxs-message-plugin</code>. You can also try it out in the iframe\nbelow.</p>\n\n<iframe src=\"https://andreas-hjortland.github.io/ngxs-message-plugin/\"></iframe>\n\n<p>In a recent project I was part of we needed to have functionality to undock parts of a web application onto separate\nmonitors for some power users which required us to be able to share the state of our application easily between the\nwindows to ensure that the state was consistent across the windows and to avoid running uneccessary requests to the\nserver to avoid longer loading times when we undocked the application.</p>\n\n<p>This was an Angular project where we are using the excelent <a href=\"https://github.com/ngxs/store\" target=\"external\"><code class=\"language-plaintext highlighter-rouge\">ngxs</code></a>\nproject to have a centralized store. This project aims to solve the same issues as redux, but it is developed with\nAngular in mind instead of beeing a generic JS library with hooks into js frameworks like redux. Since we had a\ncentralized state store, I decided we could try to synchronize the state between the windows using <code class=\"language-plaintext highlighter-rouge\">postMessage</code>. I had\nalready seen that ngxs does support plugins, so I tried to check out the\n<a href=\"https://www.ngxs.io/plugins/intro\" target=\"external\">documentation</a>. Unfortunately I found it a bit lacking, but it\nwas enough to get started. After I had looked at the documentation, I experimented a bit creating a plugin and\nintercepting incomming actions and running the actions on both the main browsing context and the child context (the\npopup).</p>\n\n<p>This worked to a degree. I was able to run the same actions on both sides, but since the state wasn’t\nsynchronized when the frame was opened so this didn’t work. Then I tried adding a method to request the initial state\nduring startup of the child instance. This worked and we got our first prototype, although I quickly realized that we\ndid all the work twice. This didn’t really make much of a difference when we were just retrieving data, but when we\nhad an action that posted data, it would run on both the main and the child window so that we committed the data twice.</p>\n\n<p>With this is mind I decided to change the architecture a bit. Instead of creating a plugin in the main window that\nsent a message to the child context so that we ran the same action in both contexts, I went for an architecture where we\nsend the the updated state whenever an action has run instead of sending the action. This also made it possible for us\nto dispatch actions on the child that triggered on the store of the main window. This worked perfectly (or so we\nthought) and we were able to handle state changes on the host and child. I implemented this by using the\n<code class=\"language-plaintext highlighter-rouge\">store.reset(...)</code> method on the child whenever we got an updated state. Unfortunately this had the unfortunate side\neffect that we would trigger every store subscriber because the state was changed. I had to create a deep merge function\n(could have used the one in lodash, but wanted to avoid uneccessary dependencies) so that we traversed the object graph\nand only changed the part of the state that actually was changed. After we had done this it worked even better, but we\nstill had one issue left.</p>\n\n<p>Whenever we dispatched an action, we could change the state in our reducer using <code class=\"language-plaintext highlighter-rouge\">setState</code> without dispatching another\naction like in redux. Since this change wasn’t coupled directly to the dispatched action, we got an issue where the\nstate would only update on the client whenever we was done loading an entity. This meant that the loaders wouldn’t show\nup in the child window. We got around this by ditching the plugin system on the host, and just subscribing to any store\nchanges. This worked perfectly as far as I could tell and we are able to synchronize the state across the windows and\nany state updates from either the host or any of it’s children will automatically propagate to each other.</p>\n\n<p>You can check out the project on my\n<a href=\"https://github.com/Andreas-Hjortland/ngxs-message-plugin/\" target=\"external\">github</a>, and install it using <code class=\"language-plaintext highlighter-rouge\">npm i\nngxs-message-plugin</code> / <code class=\"language-plaintext highlighter-rouge\">yarn add ngxs-message-plugin</code>. I have documented how to use it in the README file, but the short\nversion is to just import <code class=\"language-plaintext highlighter-rouge\">NgxsMessagePlugin.forRoot()</code> in your <code class=\"language-plaintext highlighter-rouge\">App.module.ts</code> for the host and\n<code class=\"language-plaintext highlighter-rouge\">NgxsMessagePlugin.forChild()</code> in your <code class=\"language-plaintext highlighter-rouge\">App.module.ts</code> for your child. You can see how the\n<a href=\"https://github.com/Andreas-Hjortland/ngxs-message-plugin/blob/master/projects/testapp/src/app/app.module.ts\" target=\"external\">host</a>\nand\n<a href=\"https://github.com/Andreas-Hjortland/ngxs-message-plugin/blob/master/projects/testapp/src/app/child/child.module.ts\" target=\"external\">child</a>\nmodules are implemented in the demo project.</p>\n\n<p>As a minor extra thing I implemented. I also created a <a href=\"https://hjort.land/chess\" target=\"external\">chess game</a>\n(<a href=\"https://github.com/Andreas-Hjortland/ngxs-chess\" target=\"external\">github</a>) where I used a custom transport\nimplementation utilizing WebRTC so that you can play against other players. I haven’t implemented any chess rules, but\nit was a fun experiment seeing the implementation is robust enough to actually work over the network as well.</p>\n\n",
      "date_published": "2022-06-04T00:00:00+00:00"
    },
    {
      "id": "https://hjort.land/vim/tutorial/2014/04/10/delete-lines-vim.html",
      "url": "https://hjort.land/vim/tutorial/2014/04/10/delete-lines-vim.html",
      "title": "Delete all but the last n lines in vim",
      "content_html": "<p>Just a small tidbit. If you ever need to delete all lines from the cursor, except the last n lines in Vim, you can use the ex command</p>\n\n<figure class=\"highlight\"><pre><code class=\"language-vim\" data-lang=\"vim\"><span class=\"p\">:,</span>$<span class=\"p\">-</span>nd</code></pre></figure>\n\n<p>Where n is number of lines you want to retain from the bottom. For example, if you have a footer on the last line, you can use</p>\n\n<figure class=\"highlight\"><pre><code class=\"language-vim\" data-lang=\"vim\"><span class=\"p\">:,</span>$<span class=\"m\">-1</span><span class=\"k\">d</span></code></pre></figure>\n\n<p>To delete all but the last line.</p>\n",
      "date_published": "2014-04-10T08:49:35+00:00"
    },
    {
      "id": "https://hjort.land/fetcmail/email/mail/tutorial/2014/01/26/set-up-fetchmailrc.html",
      "url": "https://hjort.land/fetcmail/email/mail/tutorial/2014/01/26/set-up-fetchmailrc.html",
      "title": "Set up fetchmailrc",
      "content_html": "<p>I have struggled a lot getting my procmailrc to work properly, so I thought I would compile what I have learned now that\nit actually works. A small disclaimer though: A more thorough reference is\n<a href=\"http://partmaps.org/era/procmail/quickref.html\" target=\"\\_blank\">here</a>, and almost all the information I have here\ncame from that page, but I have also used some tutorials scattered across the internet if it was something I didn’t\nreally understand.</p>\n\n<p>First we need to set a couple of variables which tells procmail how to behave and which folders it should use. In the\nprocmailrc the variables are stored as <code class=\"language-plaintext highlighter-rouge\">NAME=VALUE</code> and retrieved by <code class=\"language-plaintext highlighter-rouge\">$NAME</code>, much like it is in bash. The variables we\nneed are <code class=\"language-plaintext highlighter-rouge\">DEFAULT</code>, <code class=\"language-plaintext highlighter-rouge\">MAILDIR</code> and <code class=\"language-plaintext highlighter-rouge\">PMDIR</code>. It is smart to also set <code class=\"language-plaintext highlighter-rouge\">LOGFILE</code> and perhaps <code class=\"language-plaintext highlighter-rouge\">VERBOSE</code>. In my procmailrc I\nhave also set a prefix path which I then use on all the other folders. so my configuration becomes something like this:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">CORRECTHOME</span><span class=\"o\">=</span>/home/username\n\n<span class=\"nv\">MAILDIR</span><span class=\"o\">=</span><span class=\"nv\">$CORRECTHOME</span>/Maildir/\n<span class=\"nv\">DEFAULT</span><span class=\"o\">=</span><span class=\"nv\">$CORRECTHOME</span>/Maildir/\n<span class=\"nv\">PMDIR</span><span class=\"o\">=</span><span class=\"nv\">$CORRECTHOME</span>/\n<span class=\"nv\">LOGFILE</span><span class=\"o\">=</span><span class=\"nv\">$PMDIR</span>/.mail.log\n<span class=\"nv\">VERBOSE</span><span class=\"o\">=</span>off\n</code></pre></div></div>\n\n<ul>\n  <li><code class=\"language-plaintext highlighter-rouge\">CORRECTHOME</code>: This is my home folder</li>\n  <li><code class=\"language-plaintext highlighter-rouge\">MAILDIR</code>, <code class=\"language-plaintext highlighter-rouge\">DEFAULT</code>: This is where procmail should put my mail. Notice the slash after the folder name. It is needed\nfor it to use the Maildir format.  If you do not have it, it will store mail as in the mbox format.</li>\n  <li><code class=\"language-plaintext highlighter-rouge\">PMDIR</code>: This is where procmail should look for configuration files and store log files</li>\n  <li><code class=\"language-plaintext highlighter-rouge\">LOGFILE</code>: This is the path to the logfile</li>\n  <li><code class=\"language-plaintext highlighter-rouge\">VERBOSE</code>: Should procmail output verbose information to the logfile or not. I have never needed as much information\nas I get from the verbose output, so I have just turned it off.</li>\n</ul>\n\n<p>After this we get to the recipes (or rules as I like to call them, but that is not completely accurate):</p>\n\n<ul>\n  <li>A colon line which tells procmail what it should look for when filtering. The simplest colon line is just :0: (thats a\nzero, not the letter O). I haven’t used any other colon lines than that one, but it is possible to read more\n<a href=\"http://partmaps.org/era/procmail/quickref.html#colon-line\" target=\"\\_blank\">here</a>.</li>\n  <li>A condition. This can be empty or a rule. The I have only used regular expression rules, but it is possible to test on\nexit code of external programs, size of the message or to give messages scores. The regular expression I use the most\nis the parenthesis: <code class=\"language-plaintext highlighter-rouge\">* ^FROM: *(mail1@domain1.com|mail2@domain2.com)</code>\nthis will match all mail from either mail1@domain1.com or mail2@domain2.com. Note that regular expressions are by\ndefault case insensitive.</li>\n  <li>Action line. This is what procmail should do to the mail. Note that you can only have a single action line for each\ncolon line. If you need multiple actions for one colon line, you can use curly braces, but that is not something I\nhave experimented doing. An action can be either to choose where to save the mail, forward it, or pipe it through a\nshell command.</li>\n</ul>\n\n<p>Here is an example of an recipe</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>:0:\n<span class=\"k\">*</span> ^<span class=\"o\">(</span>From|Cc|To<span class=\"o\">)</span>.<span class=\"k\">*</span>@invoicia.no\n.INBOX.Invoices/\n</code></pre></div></div>\n\n<p>This will take all mails which is related to the domain invoicia.no (either from, or where they are put on CC or\nrecipient) and put it in the Maildir .INBOX.Invoices/ which is where I keep all my invoices.</p>\n\n<p>Of course you can have multiple colon lines after each other, and I will recomend you to end your procmailrc with a line\nwhich will save all the mail to the default mailbox like this:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>:0:\n<span class=\"nv\">$DEFAULT</span>\n</code></pre></div></div>\n\n<p>I hope this was helpful, and at last you will see my procmailrc (with some changes to make it a bit more anonymous and\nto make sure that email scrapers won’t start spamming everyone I know). Note the pseudo variable TO_ which I use in the\nsecond to last recipe. This will move all mails where the recipient is spam on my domain to the Spam folder in my inbox.</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">CORRECTHOME</span><span class=\"o\">=</span>/home/&lt;myusername&gt;\n<span class=\"nv\">MAILDIR</span><span class=\"o\">=</span><span class=\"nv\">$CORRECTHOME</span>/Maildir/\n<span class=\"nv\">DEFAULT</span><span class=\"o\">=</span><span class=\"nv\">$CORRECTHOME</span>/Maildir/\n<span class=\"nv\">PMDIR</span><span class=\"o\">=</span><span class=\"nv\">$CORRECTHOME</span>\n<span class=\"nv\">LOGFILE</span><span class=\"o\">=</span><span class=\"nv\">$PMDIR</span>/.mail.log\n<span class=\"nv\">VERBOSE</span><span class=\"o\">=</span>off\n\n:0:\n<span class=\"k\">*</span> ^From: <span class=\"k\">*</span><span class=\"o\">(</span>invoice@invoice.com|invoice@invoice2.com<span class=\"o\">)</span> <span class=\"k\">*</span><span class=\"err\">$</span>\n.INBOX.MyInvoices\n\n:0:\n<span class=\"k\">*</span> ^TO_<span class=\"se\">\\\\</span>&lt;spam address on hjortland.org<span class=\"se\">\\\\</span><span class=\"o\">&gt;</span>\n.INBOX.Spam/\n\n:0:\n<span class=\"nv\">$DEFAULT</span>\n</code></pre></div></div>\n\n",
      "date_published": "2014-01-26T08:49:35+00:00"
    }
    
  ]
}

