<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>nik&#x27;s site - Blog posts</title>
    <subtitle>nikhilmwarrier&#x27;s personal website and blog</subtitle>
    <link rel="self" type="application/atom+xml" href="https://nikw.in/blog/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://nikw.in/blog/"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2025-11-07T00:00:00+00:00</updated>
    <id>https://nikw.in/blog/atom.xml</id>
    <entry xml:lang="en">
        <title>Adding autotracking to a security camera</title>
        <published>2025-11-07T00:00:00+00:00</published>
        <updated>2025-11-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              nikhilmwarrier
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nikw.in/blog/v380-tracking/"/>
        <id>https://nikw.in/blog/v380-tracking/</id>
        
        <content type="html" xml:base="https://nikw.in/blog/v380-tracking/">&lt;p&gt;I recently got my hands on a generic Chinese camera.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-app-situation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-app-situation&quot; aria-label=&quot;Anchor link for: the-app-situation&quot;&gt;The App Situation&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This camera requires an extremely shady mobile app (&lt;a href=&quot;https:&#x2F;&#x2F;play.google.com&#x2F;store&#x2F;apps&#x2F;details?id=com.macrovideo.v380pro&quot;&gt;V380
Pro — Google Play&lt;&#x2F;a&gt;) to
function. This is easily among the worst pieces of software I&#x27;ve ever
interacted with. Slow, janky, and filled to the brim with ads — both regular
ads, and promotion for their &quot;cloud service&quot; to store (among other things)
camera footage.&lt;&#x2F;p&gt;
&lt;p&gt;I spent some more time in the app, looking at the capabilities that it exposes.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It &quot;implements&quot; PTZ (Pan, Tilt, Zoom), but zoom doesn&#x27;t work.&lt;&#x2F;li&gt;
&lt;li&gt;It can save&#x2F;load &lt;em&gt;preset&lt;&#x2F;em&gt; positions.&lt;&#x2F;li&gt;
&lt;li&gt;It has a mode where it&#x27;s supposed to &lt;em&gt;autotrack&lt;&#x2F;em&gt; people&#x2F;objects. Funnily
enough, the camera rotated in the &lt;strong&gt;opposite&lt;&#x2F;strong&gt; direction of the object when I
tested this.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;a-man-a-plan-a-lan&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-man-a-plan-a-lan&quot; aria-label=&quot;Anchor link for: a-man-a-plan-a-lan&quot;&gt;A man, a plan, a lan&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I wanted to do the following things:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Make the camera usable without the shitware app.&lt;&#x2F;li&gt;
&lt;li&gt;Try to fix&#x2F;implement autotracking.&lt;&#x2F;li&gt;
&lt;li&gt;Prevent it from leaking my data all over the place.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;When I dug into the settings in the app, I discovered a redeeming quality: this
wonderful little wiretap supports ONVIF (for future me, ONVIF port was 8899).&lt;&#x2F;p&gt;
&lt;p&gt;ONVIF is a standard for controlling networked cameras. This is great news! I
can implement the person-tracking functionality on my own!&lt;&#x2F;p&gt;
&lt;p&gt;I messed around with &lt;a href=&quot;https:&#x2F;&#x2F;frigate.video&#x2F;&quot;&gt;Frigate&lt;&#x2F;a&gt;, trying to get its
autotracking to work with the camera. However it just refused to work. Pan&#x2F;Tilt
worked perfectly, but autotracking did not.&lt;&#x2F;p&gt;
&lt;p&gt;I decided to take the matter into my own hands and see if I could implement
autotracking on my own.&lt;&#x2F;p&gt;
&lt;p&gt;I made the following plan:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Write two scripts:
&lt;ul&gt;
&lt;li&gt;One that exposes a function that can be used to move&#x2F;reset the camera.&lt;&#x2F;li&gt;
&lt;li&gt;Another that fetches the video stream from the camera, uses OpenCV to do
the detection, and move the camera so that it points straight at the
target. Maybe record events?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Drop all this onto a Raspberry Pi.&lt;&#x2F;li&gt;
&lt;li&gt;Use the Pi&#x27;s hotspot to connect to the camera, with very restricted internet
access.&lt;&#x2F;li&gt;
&lt;li&gt;Set up a Wireguard VPN to connect to this little network from outside.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This post will cover just the first part of this glorified plan.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;autotracking-kinda-works&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#autotracking-kinda-works&quot; aria-label=&quot;Anchor link for: autotracking-kinda-works&quot;&gt;Autotracking kinda works!&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Tracking objects is pretty easy with OpenCV. Turns out, getting the camera to
move properly is the hard part.&lt;&#x2F;p&gt;
&lt;p&gt;This is because this camera only implements a small subset of the ONVIF
standard, and that too in an annoying way.&lt;&#x2F;p&gt;
&lt;p&gt;(I got to know about this thanks to &lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;hawkeye217&#x2F;152a1d4ba80760dac95d46e143d37112&quot;&gt;this
script&lt;&#x2F;a&gt;
taken from &lt;a href=&quot;https:&#x2F;&#x2F;docs.frigate.video&#x2F;configuration&#x2F;autotracking&#x2F;#checking-onvif-camera-support&quot;&gt;Frigate&#x27;s
docs&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;The camera supports ONVIF PTZ with the following caveats:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;It supports &lt;em&gt;only&lt;&#x2F;em&gt; &lt;code&gt;ContinuousMove&lt;&#x2F;code&gt; (which is basically relative motion —
you give it X and Y velocities).&lt;&#x2F;li&gt;
&lt;li&gt;The move is executed &lt;em&gt;after&lt;&#x2F;em&gt; you call &lt;code&gt;Stop&lt;&#x2F;code&gt; on it.&lt;&#x2F;li&gt;
&lt;li&gt;There&#x27;s no way to query or set the absolute position of the camera.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I (with a lot of help from Gemini 2.5 Pro) wrote the first script,
&lt;code&gt;onvif_control.py&lt;&#x2F;code&gt;. It exposes a &lt;code&gt;move(x, y)&lt;&#x2F;code&gt; method that moves the camera in
&lt;em&gt;steps&lt;&#x2F;em&gt;. This lets me calculate how much I&#x27;m moving with some accuracy.&lt;&#x2F;p&gt;
&lt;p&gt;I also wrote the &lt;code&gt;person_tracker.py&lt;&#x2F;code&gt; script that loads the RTSP video stream
into a separate thread, and uses OpenCV (with YOLOv3) to recognise people and
incrementally move towards them.&lt;&#x2F;p&gt;
&lt;p&gt;So far, this is working fine.&lt;&#x2F;p&gt;
&lt;p&gt;The biggest problem is that there&#x27;s no way to return to a starting position.
The camera doesn&#x27;t seeem to expose any way to query the absolute position.&lt;&#x2F;p&gt;
&lt;p&gt;I tried storing the steps the camera has taken so that I can backtrack using
it, but that doesn&#x27;t work because &lt;code&gt;move()&lt;&#x2F;code&gt; might not be successful all the time
(weird lags with the camera, reaching the end of motion range, etc). It kept
throwing my calculation off, so I abandoned it for the time being.&lt;&#x2F;p&gt;
&lt;p&gt;I also tried an approach where I essentially &quot;calibrated&quot; the camera by moving
it to the extremes of its range of motion and measuring the steps it took to do
that, but that didn&#x27;t work either. This approach still holds some promise
though, so I&#x27;ll probably try this again.&lt;&#x2F;p&gt;
&lt;p&gt;My final idea is this: I can maybe reverse engineer the calls the V380 Pro app
makes to the camera, and use that proprietary protocol to control it. That&#x27;s a
lot more work, but it seems like it will be more reliable. The app has a way to
store PTZ presets, as I mentioned at the beginning. I can probably use that to
my advantage if I figure this out.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll try to clean up and publish the scripts so far if I get time.&lt;&#x2F;p&gt;
&lt;p&gt;Stay tuned for a part 2!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>keyd: Layers, macros, and remaps, even on laptop keyboards</title>
        <published>2025-10-23T00:00:00+00:00</published>
        <updated>2025-10-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              nikhilmwarrier
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nikw.in/blog/keyd-my-beloved/"/>
        <id>https://nikw.in/blog/keyd-my-beloved/</id>
        
        <content type="html" xml:base="https://nikw.in/blog/keyd-my-beloved/">&lt;p&gt;I have a mechanical keyboard (blue switches)  with a volume knob. These things
have pretty satisfying tactile feedback which makes them fun to play with.&lt;&#x2F;p&gt;
&lt;p&gt;I felt that I wasn&#x27;t using it enough, so I decided that once I get some free
time, I&#x27;ll find a way to remap it to something more useful than adjusting
volume.&lt;&#x2F;p&gt;
&lt;p&gt;On one lazy day (ie, just before one of the uni exams), I decided to look more
closely at the knob. Turns out, when it&#x27;s rotated clockwise, it simulates
pressing the &lt;kbd&gt;Volume Up&lt;&#x2F;kbd&gt; key, and vice versa counter-clockwise.&lt;&#x2F;p&gt;
&lt;p&gt;Now all I had to do was remap these keys to something more useful.&lt;&#x2F;p&gt;
&lt;p&gt;Since my keyboard doesn&#x27;t support &lt;a href=&quot;https:&#x2F;&#x2F;qmk.fm&#x2F;&quot;&gt;QMK&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;www.caniusevia.com&#x2F;&quot;&gt;VIA&lt;&#x2F;a&gt;, I had to find something that works on all keyboards.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s when I stumbled on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rvaiya&#x2F;keyd&quot;&gt;keyd&lt;&#x2F;a&gt;. It&#x27;s a
wonderful little daemon that intercepts keypresses and lets you do things like
remap buttons (&lt;kbd&gt;CapsLk&lt;&#x2F;kbd&gt; to &lt;kbd&gt;Ctrl&lt;&#x2F;kbd&gt; when held, &lt;kbd&gt;Esc&lt;&#x2F;kbd&gt;
when tapped being a common one), create layers, and more.&lt;&#x2F;p&gt;
&lt;p&gt;It works by spinning up a daemon that reads from a config file, after which it
happily does its thing in the background.&lt;&#x2F;p&gt;
&lt;p&gt;I mapped my volume knob to up&#x2F;down arrow keys by default, and left&#x2F;right
arrow keys when shift is held.&lt;&#x2F;p&gt;
&lt;p&gt;I also created a global config that lets me use
&lt;kbd&gt;AltGr&lt;&#x2F;kbd&gt;+&lt;kbd&gt;h&#x2F;j&#x2F;k&#x2F;l&lt;&#x2F;kbd&gt; instead of arrow keys (on all keyboards,
including my laptop&#x27;s). I also use &lt;kbd&gt;AltGr&lt;&#x2F;kbd&gt; as my &lt;a href=&quot;..&#x2F;compose-key&quot;&gt;compose
key&lt;&#x2F;a&gt;, which I didn&#x27;t want to break. Turns out, you can specify
that you want to &lt;em&gt;overload&lt;&#x2F;em&gt; a key in the keyd config file. This makes it behave
like normal &lt;kbd&gt;AltGr&lt;&#x2F;kbd&gt; when tapped, but changes to a special &lt;code&gt;nav&lt;&#x2F;code&gt; layer
when held. This layer then lets me use my &lt;kbd&gt;h&#x2F;j&#x2F;k&#x2F;l&lt;&#x2F;kbd&gt; keys for
navigating around.&lt;&#x2F;p&gt;
&lt;p&gt;This is what my global config looks like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;init&quot; class=&quot;language-init &quot;&gt;&lt;code class=&quot;language-init&quot; data-lang=&quot;init&quot;&gt;[ids]
*

[main]

# AltGr will be RightAlt&amp;#x2F;Compose on press, nav layer on hold. 
altgr = overload(nav, rightalt)

[nav]
h = left
j = down
k = up
l = right
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And this is what the config for my mechanical keyboard looks like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ini&quot; class=&quot;language-ini &quot;&gt;&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;[ids]
1a2c:7fff:3c9dd5ab

[main]
volumeup = down
volumedown = up

[shift]
volumeup = right
volumedown = left

[control]
volumeup = volumeup
volumedown = volumedown

[alt]
volumeup = brightnessup
volumedown = brightnessdown
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Dynamic MathJax typesetting in Svelte</title>
        <published>2024-07-31T00:00:00+00:00</published>
        <updated>2024-07-31T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              nikhilmwarrier
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nikw.in/blog/dynamic-mathjax-svelte/"/>
        <id>https://nikw.in/blog/dynamic-mathjax-svelte/</id>
        
        <content type="html" xml:base="https://nikw.in/blog/dynamic-mathjax-svelte/">&lt;p&gt;&lt;em&gt;(TL;DR;give-me-the-code: &lt;a href=&quot;https:&#x2F;&#x2F;svelte.dev&#x2F;repl&#x2F;b108f967c79243eb9dbd75cffa3cf23d?version=4.2.18&quot;&gt;Dynamic MathJax rendering — Svelte REPL&lt;&#x2F;a&gt;)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I am building a quiz platform for the startup I am working on right now. The
questions in the quiz often contain LaTeX math elements which need to be typeset
by MathJax.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-setup&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-setup&quot; aria-label=&quot;Anchor link for: the-setup&quot;&gt;The setup&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;MathJax and its config is brought in with the &lt;code&gt;&amp;lt;svelte:head&amp;gt;&lt;&#x2F;code&gt; tag, like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&amp;lt;svelte:head&amp;gt;
  &amp;lt;script&amp;gt;
    window.MathJax = {
      tex: {
        inlineMath: [
          [&amp;quot;$&amp;quot;, &amp;quot;$&amp;quot;],
          [&amp;quot;\\(&amp;quot;, &amp;quot;\\)&amp;quot;],
        ],
        displayMath: [[&amp;quot;$$&amp;quot;, &amp;quot;$$&amp;quot;]],
      },
      svg: {
        fontCache: &amp;quot;global&amp;quot;,
      },
      startup: {
        typeset: false,
      },
    };
  &amp;lt;&amp;#x2F;script&amp;gt;
  &amp;lt;script
    id=&amp;quot;MathJax-script&amp;quot;
    defer
    src=&amp;quot;https:&amp;#x2F;&amp;#x2F;cdn.jsdelivr.net&amp;#x2F;npm&amp;#x2F;mathjax@3&amp;#x2F;es5&amp;#x2F;tex-chtml.js&amp;quot;
  &amp;gt;&amp;lt;&amp;#x2F;script&amp;gt;
&amp;lt;&amp;#x2F;svelte:head&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I have an array of questions (which are just HTML text with LaTeX expressions)
and a variable called &lt;code&gt;current&lt;&#x2F;code&gt;, which keeps track of the index of the current
question. There are also couple of buttons to switch between the questions.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&amp;lt;script&amp;gt;
  const current = 0;
  const questions = [
    `This is $ e^x $: &amp;lt;br &amp;#x2F;&amp;gt; $$ {\\bf{e}}^x = \\sum\\limits_{n = 0}^\\infty {\\frac{{{x^n}}}{{n!}}}	$$`,
    `And this is $ \\cos x $: &amp;lt;br &amp;#x2F;&amp;gt;  $$ \\cos x = \\sum\\limits_{n = 0}^\\infty {\\frac{{{{\\left( { - 1} \\right)}^n}{x^{2n}}}}{{\\left( {2n} \\right)!}}} $$`,
    `Finally, this is $ \\sin x $: &amp;lt;br &amp;#x2F;&amp;gt; $$ \\sin x = \\sum\\limits_{n = 0}^\\infty {\\frac{{{{\\left( { - 1} \\right)}^n}{x^{2n + 1}}}}{{\\left( {2n + 1} \\right)!}}} $$`,
  ];
&amp;lt;&amp;#x2F;script&amp;gt;

&amp;lt;svelte:head&amp;gt;
  ...
&amp;lt;&amp;#x2F;svelte:head&amp;gt;


&amp;lt;main&amp;gt;
	&amp;lt;button on:click={() =&amp;gt; current--}&amp;gt;Prev&amp;lt;&amp;#x2F;button&amp;gt;
	&amp;lt;button on:click={() =&amp;gt; current++}&amp;gt;Next&amp;lt;&amp;#x2F;button&amp;gt;

	&amp;lt;p&amp;gt; {@html questions[current] }	&amp;lt;&amp;#x2F;p&amp;gt;
&amp;lt;&amp;#x2F;main&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;nikw.in&#x2F;blog&#x2F;dynamic-mathjax-svelte&#x2F;initial.gif&quot; alt=&quot;GIF showing what the initial setup looks like&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-problem&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-problem&quot; aria-label=&quot;Anchor link for: the-problem&quot;&gt;The problem&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;MathJax runs the typesetter just once, as soon as it&#x27;s loaded on the page. At
this stage, the question text has not loaded in yet, so the typesetting doesn&#x27;t
take effect.&lt;&#x2F;p&gt;
&lt;p&gt;We need a way to make this &lt;em&gt;dynamic&lt;&#x2F;em&gt;, so that MathJax will re-typeset the text
when the question number (i.e., value of &lt;code&gt;current&lt;&#x2F;code&gt;) changes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-solution&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-solution&quot; aria-label=&quot;Anchor link for: the-solution&quot;&gt;The solution&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;We can use two nifty tools that Svelte provides — the &lt;code&gt;use&lt;&#x2F;code&gt; directive and the
&lt;code&gt;{#key}&lt;&#x2F;code&gt; block — to solve this problem.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;implementing-use-typesetmath&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#implementing-use-typesetmath&quot; aria-label=&quot;Anchor link for: implementing-use-typesetmath&quot;&gt;Implementing &lt;code&gt;use:typesetMath&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;svelte.dev&#x2F;docs&#x2F;element-directives#use-action&quot;&gt;The &lt;code&gt;use&lt;&#x2F;code&gt; directive&lt;&#x2F;a&gt;
calls the attached function (called an &lt;em&gt;action&lt;&#x2F;em&gt;) when the element is created.&lt;&#x2F;p&gt;
&lt;p&gt;We can use this to create an action that will check if MathJax has been loaded
and run the formatter (&lt;code&gt;window.MathJax.typesetPromise&lt;&#x2F;code&gt;) on our element.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&amp;lt;script&amp;gt;
  &amp;#x2F;&amp;#x2F; ... &amp;#x2F;&amp;#x2F;

  &amp;#x2F;&amp;#x2F; Action functions accept the HTML element as an argument when they are called
  function typesetMath(node) {
    const typesetPromise = window.MathJax.typesetPromise;

    &amp;#x2F;&amp;#x2F; Check if MathJax has actually loaded before calling `typesetPromise`
    if (typesetPromise) typesetPromise([node]);
  }

  &amp;#x2F;&amp;#x2F;...&amp;#x2F;&amp;#x2F;
&amp;lt;&amp;#x2F;script&amp;gt;

...

&amp;lt;p use:typesetMath&amp;gt;{@html questions[current] }&amp;lt;&amp;#x2F;p&amp;gt;
...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, we need to make sure that this action is fired whenever the variable
&lt;code&gt;current&lt;&#x2F;code&gt; changes.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;setting-up-the-key-block&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#setting-up-the-key-block&quot; aria-label=&quot;Anchor link for: setting-up-the-key-block&quot;&gt;Setting up the &lt;code&gt;{#key}&lt;&#x2F;code&gt; block&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;{#key}&lt;&#x2F;code&gt; block will cause the elements inside it to re-render whenever the
&lt;em&gt;key&lt;&#x2F;em&gt; (in this case, the variable &lt;code&gt;current&lt;&#x2F;code&gt;) changes, which then goes ahead and
triggers the &lt;code&gt;typesetMath&lt;&#x2F;code&gt; action.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;{#key current}
&amp;lt;p use:typesetMath&amp;gt;{@html questions[current] }&amp;lt;&amp;#x2F;p&amp;gt;
{&amp;#x2F;key}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So far so good! Everything should work more-or-less as expected now, but there
is one little bug.&lt;&#x2F;p&gt;
&lt;p&gt;The typesetting might not happen properly for the question that is shown by
default, because MathJax might not have been loaded by the time the &lt;code&gt;typesetMath&lt;&#x2F;code&gt;
call is made, and the function exits without doing anything.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;nikw.in&#x2F;blog&#x2F;dynamic-mathjax-svelte&#x2F;first-qn-broken.gif&quot; alt=&quot;GIF showing that typesetting for the first question is broken.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To fix this, we can use the &lt;code&gt;else&lt;&#x2F;code&gt; branch in the conditional in &lt;code&gt;typesetMath&lt;&#x2F;code&gt; to
call itself recursively (with a small delay) until MathJax is loaded.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;function typesetMath(node) {
  const typesetPromise = window.MathJax.typesetPromise;
  if (typesetPromise) typesetPromise([node]);
  &amp;#x2F;&amp;#x2F; recursively call itself every 500ms until MathJax loads
  else setTimeout(() =&amp;gt; typesetMath(node), 500);
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And that&#x27;s it! That&#x27;s how I hacked it together. It is not an elegant fix but it
works pretty well for now.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;nikw.in&#x2F;blog&#x2F;dynamic-mathjax-svelte&#x2F;final.gif&quot; alt=&quot;GIF showing MathJax typesetting everything dynamically.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;details&gt;
&lt;summary&gt; Click here for the full code. &lt;&#x2F;summary&gt;
&lt;pre data-lang=&quot;html&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&amp;lt;script&amp;gt;
	import { afterUpdate } from &amp;#x27;svelte&amp;#x27;;

	let current = 0;
	const questions = [
	`This is $ e^x $: &amp;lt;br &amp;#x2F;&amp;gt; $$ 	{\\bf{e}}^x = \\sum\\limits_{n = 0}^\\infty {\\frac{{{x^n}}}{{n!}}}		$$`,
	`And this is $ \\cos x $: &amp;lt;br &amp;#x2F;&amp;gt;  $$ \\cos x = \\sum\\limits_{n = 0}^\\infty {\\frac{{{{\\left( { - 1} \\right)}^n}{x^{2n}}}}{{\\left( {2n} \\right)!}}} $$`,
	`Finally, this is $ \\sin x $: &amp;lt;br &amp;#x2F;&amp;gt; $$ \\sin x = \\sum\\limits_{n = 0}^\\infty {\\frac{{{{\\left( { - 1} \\right)}^n}{x^{2n + 1}}}}{{\\left( {2n + 1} \\right)!}}} $$`
	]

	function typesetMath(node) {
		const typesetPromise = window.MathJax.typesetPromise;
		if (typesetPromise) typesetPromise([ node ]);
		else setTimeout(() =&amp;gt; typesetMath(node), 500)
	}
&amp;lt;&amp;#x2F;script&amp;gt;

&amp;lt;svelte:head&amp;gt;
	&amp;lt;script&amp;gt;
		window.MathJax = {
			tex: {
				inlineMath: [
					[&amp;#x27;$&amp;#x27;, &amp;#x27;$&amp;#x27;],
					[&amp;#x27;\\(&amp;#x27;, &amp;#x27;\\)&amp;#x27;]
				],
				displayMath: [[&amp;#x27;$$&amp;#x27;, &amp;#x27;$$&amp;#x27;]]
			},
			svg: {
				fontCache: &amp;#x27;global&amp;#x27;
			},
			startup: {
				typeset: false
			}
		};
	&amp;lt;&amp;#x2F;script&amp;gt;
	&amp;lt;script
		id=&amp;quot;MathJax-script&amp;quot;
		defer
		src=&amp;quot;https:&amp;#x2F;&amp;#x2F;cdn.jsdelivr.net&amp;#x2F;npm&amp;#x2F;mathjax@3&amp;#x2F;es5&amp;#x2F;tex-chtml.js&amp;quot;
	&amp;gt;&amp;lt;&amp;#x2F;script&amp;gt;
&amp;lt;&amp;#x2F;svelte:head&amp;gt;

&amp;lt;main&amp;gt;
	&amp;lt;button disabled={current===0} on:click={() =&amp;gt; current--}&amp;gt;Prev&amp;lt;&amp;#x2F;button&amp;gt;
	&amp;lt;button disabled={current===questions.length-1} on:click={() =&amp;gt; current++}&amp;gt;Next&amp;lt;&amp;#x2F;button&amp;gt;

	{#key current}
	&amp;lt;p use:typesetMath&amp;gt;
		{@html questions[current] }
	&amp;lt;&amp;#x2F;p&amp;gt;
	{&amp;#x2F;key}
&amp;lt;&amp;#x2F;main&amp;gt;

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;details&gt;
&lt;p&gt;I have also created a
&lt;a href=&quot;https:&#x2F;&#x2F;svelte.dev&#x2F;repl&#x2F;b108f967c79243eb9dbd75cffa3cf23d?version=4.2.18&quot;&gt;Svelte REPL&lt;&#x2F;a&gt;
if you want to play around with this.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Escaping the Doomscroll</title>
        <published>2024-06-24T00:00:00+00:00</published>
        <updated>2024-06-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              nikhilmwarrier
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nikw.in/blog/doomscroll/"/>
        <id>https://nikw.in/blog/doomscroll/</id>
        
        <content type="html" xml:base="https://nikw.in/blog/doomscroll/">&lt;p&gt;(Warning: Ramble ahead!)&lt;&#x2F;p&gt;
&lt;p&gt;You probably know at least one person who spends hours and hours scrolling on
their phone, most probably consuming content that provides barely anything of
entertainment value, let alone something actually stimulating. Maybe &lt;em&gt;you&lt;&#x2F;em&gt; are
one of those poor souls who have sold their souls to The Algorithm, succumbing
to its infinite feed.&lt;&#x2F;p&gt;
&lt;p&gt;Algorithmically curated, infinitely-scrolling feeds are an unfortunate staple of
pretty much all the major “social” media platforms (henceforth called
“antisocial media” because that’s more accurate in my opinion). As you probably
already know, these antisocial media platforms make money by showing ads.
Targeted ads. And how are they targeted? By carefully analysing every little
move you make and every word you say on their platforms and beyond, and building
a profile out of it that knows more about you than your closest family does.
&lt;a href=&quot;https:&#x2F;&#x2F;www.forbes.com&#x2F;sites&#x2F;kashmirhill&#x2F;2012&#x2F;02&#x2F;16&#x2F;how-target-figured-out-a-teen-girl-was-pregnant-before-her-father-did&#x2F;&quot;&gt;Data mining is scary&lt;&#x2F;a&gt;
but that’s a discussion for another day.&lt;&#x2F;p&gt;
&lt;p&gt;It is obvious that it is in the antisocial media platforms’ best interests to
keep you on their platforms for as long as humanly possible, and what better way
to do it than target the novelty-seeking nature of our brains!&lt;&#x2F;p&gt;
&lt;p&gt;Users can’t really be blamed for being addicted to this infinite-scrolling
conveyor belt to hell, as these are carefully crafted by psychology and
neuroscience experts to keep you hooked for as long as possible. Addiction is
not a bug here, it is the biggest feature.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;facebook-twitter-instagram-etc&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#facebook-twitter-instagram-etc&quot; aria-label=&quot;Anchor link for: facebook-twitter-instagram-etc&quot;&gt;Facebook, Twitter, Instagram, etc&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I am happy to say that I never had an account on Facebook or Instagram, and I
won’t be making any in the near future if I can help it. Even if I do make an
account due to real-life social reasons (unfortunately that is a possibility
thanks to the network effect, the same reason why I use WhatsApp) I believe that
I know better than to scroll on it all day and progressively ruin my general
mood, as I’ve seen so many people do.&lt;&#x2F;p&gt;
&lt;p&gt;I did make a Twitter account once, just out of curiosity. I don’t think I have
access to it or the email I used to create it any more. I barely used it for a
day so I don’t think it really counts either.&lt;&#x2F;p&gt;
&lt;p&gt;Seriously, the best option is to simply never touch these unless you
&lt;em&gt;absolutely&lt;&#x2F;em&gt; have to. (eg: promoting a business, unreasonable acquaintances)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tiktok&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tiktok&quot; aria-label=&quot;Anchor link for: tiktok&quot;&gt;TikTok&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Brainrot. Banned in my country.&lt;&#x2F;p&gt;
&lt;p&gt;Although the moral implications of the government exercising control and banning
software from app stores are pretty chilling, I’m happy to say that TikTok will
not be missed. At all.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;youtube-shorts&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#youtube-shorts&quot; aria-label=&quot;Anchor link for: youtube-shorts&quot;&gt;YouTube Shorts&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Several apps tried to fill the pathetic void left by TikTok in my country. The
only one that actually succeeded was YouTube with its YouTube Shorts. This
steaming pile of garbage is still pushed very aggresively in the official
YouTube app.&lt;&#x2F;p&gt;
&lt;p&gt;I had switched to &lt;a href=&quot;https:&#x2F;&#x2F;newpipe.net&quot;&gt;NewPipe&lt;&#x2F;a&gt; long before YouTube Shorts was
even a thing. Shorts show up like normal videos on the NewPipe app, as opposed
to the infinite strand of spaghetti in the official YouTube app that brings you
closer and closer to the Singularity with every scroll.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;miscellaneous&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#miscellaneous&quot; aria-label=&quot;Anchor link for: miscellaneous&quot;&gt;Miscellaneous&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I had an account on Reddit, and I had a tendency to doomscroll on it. However
the Reddit API changes that broke third-party clients cured my addiction. The
official Reddit client is so broken and unusable that it’s impossible to get
addicted. Good job, Reddit!&lt;&#x2F;p&gt;
&lt;p&gt;I do have an account on Lemmy and Mastodon. The Lemmy one was made right after
the Reddit API disaster and I use it to occassionally look at some memes. Lemmy
is still pretty small, so it doesn’t take me long to exhaust the supply of memes
and I can put my phone down afterwards. I barely use Mastodon. It’s too messy
for my taste.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;If you have the attention span to read all the way until here, you’re doing just
fine. Your brain hasn’t completely decomposed yet and your soul is still within
you and has not been sucked away into the depths of some algorithmic feed.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks for reading!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Adding a search engine to Firefox</title>
        <published>2024-05-23T00:00:00+00:00</published>
        <updated>2024-05-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              nikhilmwarrier
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nikw.in/blog/firefox-custom-search-engine/"/>
        <id>https://nikw.in/blog/firefox-custom-search-engine/</id>
        
        <content type="html" xml:base="https://nikw.in/blog/firefox-custom-search-engine/">&lt;p&gt;Search engines in Firefox are specified by either
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;OpenSearch&quot;&gt;OpenSearch&lt;&#x2F;a&gt; tags or Firefox Add-ons.
This makes it really difficult to add site-specific search engines that don&#x27;t
support either of these (like
&lt;a href=&quot;https:&#x2F;&#x2F;hn.algolia.com&#x2F;?q=vim+is+better+than+emacs&quot;&gt;Hacker News&#x27;s site search&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;This is quite different from most Chromium-based browsers, where you can add any
site-search engine by specifying its URL with a &lt;code&gt;%s&lt;&#x2F;code&gt;, which is then substituted
with the actual search query.&lt;&#x2F;p&gt;
&lt;p&gt;However there is a way to replicate this behaviour in Firefox, although it is
hidden behind a flag and not exactly straightforward to enable.&lt;&#x2F;p&gt;
&lt;p&gt;This is how you can enable it in Firefox:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;nikw.in&#x2F;blog&#x2F;firefox-custom-search-engine&#x2F;about-config-entry.png&quot; alt=&quot;A screenshot of a Firefox window, showing the browser.urlbar.update2.engineAliasRefresh option in about:config&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open about:config&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Type in the following in the search bar:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;browser.urlbar.update2.engineAliasRefresh
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Make sure that &lt;code&gt;boolean&lt;&#x2F;code&gt; is selected, click on the + button, and set it to
&lt;code&gt;true&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Done!&lt;&#x2F;p&gt;
&lt;p&gt;Now, when you navigate to Settings &amp;gt; Search (about:preferences#search), you&#x27;ll
find a new “Add” button under the Search Shortcuts list. Just click on it to add
a new search engine.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;nikw.in&#x2F;blog&#x2F;firefox-custom-search-engine&#x2F;add-search-engine.png&quot; alt=&quot;A screenshot of a Firefox window, showing the dialog for adding a new search engine&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The Compose Key: Typing alternate characters the easy way</title>
        <published>2024-05-19T00:00:00+00:00</published>
        <updated>2024-05-19T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              nikhilmwarrier
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nikw.in/blog/compose-key/"/>
        <id>https://nikw.in/blog/compose-key/</id>
        
        <content type="html" xml:base="https://nikw.in/blog/compose-key/">&lt;p&gt;&lt;em&gt;(Note: I&#x27;ve mostly used UK&#x2F;US English keyboards and my article might be biased
towards these layouts.)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;How do you type the capital letter H?&lt;&#x2F;p&gt;
&lt;p&gt;I think it is safe to assume that you press &lt;kbd&gt;Shift&lt;&#x2F;kbd&gt; &lt;kbd&gt;h&lt;&#x2F;kbd&gt;,
instead of navigating to the
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Horse&quot;&gt;Wikipedia page for Horse&lt;&#x2F;a&gt; and copying “H”
from there.&lt;&#x2F;p&gt;
&lt;p&gt;How do you type, say, the ñ in España, or the ç in Françoise?&lt;&#x2F;p&gt;
&lt;p&gt;You don&#x27;t have to visit their respective Wikipedia pages and copy the word from
there like I used to do.&lt;&#x2F;p&gt;
&lt;p&gt;Just use the compose key.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;compose-key-i-can-t-find-that-on-my-keyboard&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#compose-key-i-can-t-find-that-on-my-keyboard&quot; aria-label=&quot;Anchor link for: compose-key-i-can-t-find-that-on-my-keyboard&quot;&gt;Compose key? I can&#x27;t find that on my keyboard!&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;You might have to download some additional software to set up compose key on
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;samhocevar&#x2F;wincompose&quot;&gt;Windows&lt;&#x2F;a&gt; or
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gnarf&#x2F;osx-compose-key&quot;&gt;macOS&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;On most Linux desktops, however, it is pretty straightforward. You might find
these guides useful:
&lt;a href=&quot;https:&#x2F;&#x2F;userbase.kde.org&#x2F;Tutorials&#x2F;ComposeKey&#x2F;en#Current_KDE_Configuration&quot;&gt;KDE Plasma&lt;&#x2F;a&gt;,
&lt;a href=&quot;https:&#x2F;&#x2F;help.gnome.org&#x2F;users&#x2F;gnome-help&#x2F;stable&#x2F;tips-specialchars.html.en&quot;&gt;GNOME&lt;&#x2F;a&gt;,
&lt;a href=&quot;https:&#x2F;&#x2F;www.setphaserstostun.org&#x2F;posts&#x2F;setting-the-compose-key-on-xfce&#x2F;&quot;&gt;XFCE&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;(For reference, I&#x27;m on KDE Plasma and I have my &lt;kbd&gt;AltGr&lt;&#x2F;kbd&gt; mapped to
&lt;kbd&gt;Compose&lt;&#x2F;kbd&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;With the compose key, ñ becomes &lt;kbd&gt;Compose&lt;&#x2F;kbd&gt; &lt;kbd&gt;n&lt;&#x2F;kbd&gt; &lt;kbd&gt;~&lt;&#x2F;kbd&gt;,
and ç becomes &lt;kbd&gt;Compose&lt;&#x2F;kbd&gt; &lt;kbd&gt;c&lt;&#x2F;kbd&gt; &lt;kbd&gt;,&lt;&#x2F;kbd&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Compose key mappings are pretty intuitive. Let&#x27;s say I want to type this: ø. Now
what does that look like? An &#x27;o&#x27; with a slash through it? Yep!
&lt;kbd&gt;Compose&lt;&#x2F;kbd&gt; &lt;kbd&gt;o&lt;&#x2F;kbd&gt; &lt;kbd&gt;&#x2F;&lt;&#x2F;kbd&gt; will give you a ø.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;compose-key-mappings&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#compose-key-mappings&quot; aria-label=&quot;Anchor link for: compose-key-mappings&quot;&gt;Compose key mappings&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;You can create your own compose key mappings by defining them in the
&lt;code&gt;~&#x2F;.Xcompose&lt;&#x2F;code&gt; file, but I have personally found this to be a bit finicky.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;some-default-mappings-that-i-often-use&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#some-default-mappings-that-i-often-use&quot; aria-label=&quot;Anchor link for: some-default-mappings-that-i-often-use&quot;&gt;Some default mappings that I often use&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Superscript numbers: &lt;kbd&gt;Compose&lt;&#x2F;kbd&gt; &lt;kbd&gt;^&lt;&#x2F;kbd&gt; &lt;kbd&gt;8&lt;&#x2F;kbd&gt; for ⁸&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;smartquotesforsmartpeople.com&#x2F;&quot;&gt;Smart quotes&lt;&#x2F;a&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;Compose&lt;&#x2F;kbd&gt; &lt;kbd&gt;&amp;lt;&lt;&#x2F;kbd&gt; &lt;kbd&gt;&quot;&lt;&#x2F;kbd&gt; for “&lt;&#x2F;li&gt;
&lt;li&gt;&lt;kbd&gt;Compose&lt;&#x2F;kbd&gt; &lt;kbd&gt;&amp;gt;&lt;&#x2F;kbd&gt; &lt;kbd&gt;&quot;&lt;&#x2F;kbd&gt; for ”&lt;&#x2F;li&gt;
&lt;li&gt;&lt;kbd&gt;Compose&lt;&#x2F;kbd&gt; &lt;kbd&gt;&amp;lt;&lt;&#x2F;kbd&gt; &lt;kbd&gt;&#x27;&lt;&#x2F;kbd&gt; for ‘&lt;&#x2F;li&gt;
&lt;li&gt;&lt;kbd&gt;Compose&lt;&#x2F;kbd&gt; &lt;kbd&gt;&amp;gt;&lt;&#x2F;kbd&gt; &lt;kbd&gt;&#x27;&lt;&#x2F;kbd&gt; for ’&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Dashes
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;Compose&lt;&#x2F;kbd&gt; &lt;kbd&gt;-&lt;&#x2F;kbd&gt; &lt;kbd&gt;-&lt;&#x2F;kbd&gt; &lt;kbd&gt;-&lt;&#x2F;kbd&gt; for — (an em dash)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;kbd&gt;Compose&lt;&#x2F;kbd&gt; &lt;kbd&gt;-&lt;&#x2F;kbd&gt; &lt;kbd&gt;-&lt;&#x2F;kbd&gt; &lt;kbd&gt;.&lt;&#x2F;kbd&gt; for – (an en dash)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;kbd&gt;Compose&lt;&#x2F;kbd&gt; &lt;kbd&gt;&amp;lt;&lt;&#x2F;kbd&gt; &lt;kbd&gt;3&lt;&#x2F;kbd&gt; for ♥&lt;&#x2F;li&gt;
&lt;li&gt;&lt;kbd&gt;Compose&lt;&#x2F;kbd&gt; &lt;kbd&gt;:&lt;&#x2F;kbd&gt; &lt;kbd&gt;)&lt;&#x2F;kbd&gt; for ☺&lt;&#x2F;li&gt;
&lt;li&gt;&lt;kbd&gt;Compose&lt;&#x2F;kbd&gt; &lt;kbd&gt;:&lt;&#x2F;kbd&gt; &lt;kbd&gt;(&lt;&#x2F;kbd&gt; for ☹&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#further-reading&quot; aria-label=&quot;Anchor link for: further-reading&quot;&gt;Further reading&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Compose_key&quot;&gt;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Compose_key&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kragen&#x2F;xcompose&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;kragen&#x2F;xcompose&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;canonical.org&#x2F;~kragen&#x2F;setting-up-keyboard.html&quot;&gt;http:&#x2F;&#x2F;canonical.org&#x2F;~kragen&#x2F;setting-up-keyboard.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>On writing</title>
        <published>2024-04-17T00:00:00+00:00</published>
        <updated>2024-04-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              nikhilmwarrier
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nikw.in/blog/on-writing/"/>
        <id>https://nikw.in/blog/on-writing/</id>
        
        <content type="html" xml:base="https://nikw.in/blog/on-writing/">&lt;h2 id=&quot;categorising-writing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#categorising-writing&quot; aria-label=&quot;Anchor link for: categorising-writing&quot;&gt;Categorising writing&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I noticed that my writings fall on a gradient, whose two extremes are:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Writing purely for myself&lt;&#x2F;li&gt;
&lt;li&gt;Writing purely for someone else&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;On one end of the spectrum, I have things like journals and notes and todo lists
and the like. On the other end, I have academic pieces and tutorials.&lt;&#x2F;p&gt;
&lt;p&gt;This blog falls squarely in the middle.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;writing-as-an-emotional-outlet&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#writing-as-an-emotional-outlet&quot; aria-label=&quot;Anchor link for: writing-as-an-emotional-outlet&quot;&gt;Writing as an emotional outlet&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Journalling. It really makes me calm down and allows me to reflect on things
properly.&lt;&#x2F;p&gt;
&lt;p&gt;Things almost always seem bigger than they are when you are dealing with them in
your head in real time.&lt;&#x2F;p&gt;
&lt;p&gt;I discovered that if something is troubling me, I can just write it down and
come back to it a few hours later. When I look at it again after a few hours,
the problem will have magically become smaller and more solvable.&lt;&#x2F;p&gt;
&lt;p&gt;It keeps my sanity in check (to an extent, of course).&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t use anything fancy for journalling either. Just Markdown files.&lt;&#x2F;p&gt;
&lt;p&gt;I did consider a paper journal but it&#x27;s not easy to back those up, plus typing
is faster anyway.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;writing-a-blog&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#writing-a-blog&quot; aria-label=&quot;Anchor link for: writing-a-blog&quot;&gt;Writing a blog&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Actually writing a proper blog is a new experience for me. Lately most of my
work has been on the extremes of the categorising that I described at the
beginning (Writing purely for myself or writing purely for someone else).&lt;&#x2F;p&gt;
&lt;p&gt;In the first case, I have complete freedom of expression without regards to any
consequences, and I can write without having to think about style or consistency
or whether I will offend someone. It is an open game and I am the only player.
But because of obvious reasons, I cannot share or publish it.&lt;&#x2F;p&gt;
&lt;p&gt;In the second case, my writing invariably becomes drab and depersonalised and as
painful to read as it is to write.&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to see if I can strike a balance between the two. I want to improve the
way I present my thought processes to others. I feel like this is a very
essential skill, and I have a lot of room for improvement in this department.&lt;&#x2F;p&gt;
&lt;p&gt;I feel that one&#x27;s ideas, no matter how great they might be, are ultimately
useless if no one else can understand it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-i-write&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-i-write&quot; aria-label=&quot;Anchor link for: how-i-write&quot;&gt;How I write&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I use one of those &lt;a href=&quot;https:&#x2F;&#x2F;nikw.in&#x2F;blog&#x2F;on-writing&#x2F;lcd-tablet.jpg&quot;&gt;cheap LCD tablets&lt;&#x2F;a&gt; for more short-term
content, like diagrams for brainstorming. (And also for solving Physics problems
:-P)&lt;&#x2F;p&gt;
&lt;p&gt;For anything more concrete, I use my computer.&lt;&#x2F;p&gt;
&lt;p&gt;I typically write in Markdown using Neovim. As I can type faster than I can
write with a pen on paper, it happens to be by far the fastest and most
efficient way I&#x27;ve found to get large amounts of text out of my brain and onto a
more permanent storage medium.&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes, when I&#x27;m working on something, I get ideas for topics that are not
quite relevant to what I was working on. I usually throw these to a file called
something like &lt;code&gt;~&#x2F;ideas.md&lt;&#x2F;code&gt;, and review them when I get time.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve found this to be an excellent strategy to not lose good ideas, while also
being able to focus on what I am doing instead of chasing the shiny new idea.&lt;&#x2F;p&gt;
&lt;p&gt;Speaking of which, I should also write a post on distraction. More specifically
my concept of “return-on-distraction”.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ echo &amp;quot;Write blog post about return-on-distraction&amp;quot; &amp;gt; ~&amp;#x2F;ideas.md
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Added to the list!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Setting up Zola for fun and profit. Also, hello world!</title>
        <published>2024-04-17T00:00:00+00:00</published>
        <updated>2024-04-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              nikhilmwarrier
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://nikw.in/blog/setting-up-zola/"/>
        <id>https://nikw.in/blog/setting-up-zola/</id>
        
        <content type="html" xml:base="https://nikw.in/blog/setting-up-zola/">&lt;p&gt;I&#x27;ve made a lot of blogs, but I never really maintained any of them as I would
lose interest after the first couple of posts or so.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s hope things are different this time!&lt;&#x2F;p&gt;
&lt;p&gt;With that out of the way, let&#x27;s talk about the cool stuff.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zola&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#zola&quot; aria-label=&quot;Anchor link for: zola&quot;&gt;Zola&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This blog (as of writing) is built on the &lt;a href=&quot;https:&#x2F;&#x2F;jamstack.org&#x2F;&quot;&gt;Jamstack&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I am using a static-site generator called &lt;a href=&quot;https:&#x2F;&#x2F;getzola.org&quot;&gt;Zola&lt;&#x2F;a&gt;. I write
my posts in Markdown, and Zola converts them to HTML pages using some templates.
And it&#x27;s &lt;em&gt;fast&lt;&#x2F;em&gt;. Right now, with only one post, it takes just 6ms to generate
the whole site!&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve used &lt;a href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;www.11ty.dev&#x2F;&quot;&gt;11ty&lt;&#x2F;a&gt; in the
past, but they felt a bit overkill for my little blog and I wanted to try
something different and preferably a bit more minimal.&lt;&#x2F;p&gt;
&lt;p&gt;I was actually planning to cobble together my own static-site generator using
&lt;code&gt;pandoc&lt;&#x2F;code&gt; and some shell scripts, but &lt;a href=&quot;https:&#x2F;&#x2F;vicfic.neocities.org&quot;&gt;VicFic&lt;&#x2F;a&gt;
saved my sanity and suggested Zola.&lt;&#x2F;p&gt;
&lt;p&gt;So, I followed Zola&#x27;s getting-started guide and set things up. I wrote my own
CSS and I ported over my old homepage to Zola. It was pretty smooth and less
things went wrong than I had anticipated.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;styling&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#styling&quot; aria-label=&quot;Anchor link for: styling&quot;&gt;Styling&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m using &lt;a href=&quot;https:&#x2F;&#x2F;sass-lang.com&#x2F;&quot;&gt;SCSS&lt;&#x2F;a&gt; to write the stylesheets. The font is
&lt;a href=&quot;https:&#x2F;&#x2F;docs.xz.style&#x2F;fonts&#x2F;ibm-plex&#x2F;ibm-plex-mono&quot;&gt;IBM Plex Mono&lt;&#x2F;a&gt; which I got
from &lt;a href=&quot;https:&#x2F;&#x2F;fonts.xz.style&quot;&gt;xz&#x2F;fonts&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hosting&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hosting&quot; aria-label=&quot;Anchor link for: hosting&quot;&gt;Hosting&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s hosted on the wonderful &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.page&#x2F;&quot;&gt;Codeberg Pages&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;pun&amp;gt;&lt;&#x2F;code&gt;&lt;br &#x2F;&gt;
I&#x27;m terrible at making conclusions.&lt;br &#x2F;&gt;
&lt;code&gt;&amp;lt;&#x2F;pun&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
