xam.iosBlog of Max Kleucker.2024-02-18T16:25:00Zhttps://xam.io/Max Kleuckermax@xam.ioIn-Range Dependency Updates with Renovate2024-02-18T16:25:00Zhttps://xam.io/2024/renovate-range/<p><a href="https://docs.renovatebot.com/">Renovate</a> is a tool to keep dependencies up-to-date in your software development projects. By default it is very eager to update dependencies regardless of defined ranges. This way you can avoid that updates keep pilling up.</p>
<p>I have a few internal packages that are actively hardened in different, parallel software releases. To better align for different teams working on those, different minor versions are being tracked per software release. For example <code>productA</code> relies <code>@internal/package@1.3.x</code> for an ongoing release, and <code>productB</code> tracks <code>@internal/package@1.4.x</code>. And to increase complexity newer features already land in the <code>@internal/package@1.5.x</code> versions.</p>
<p>I want the updates to be automatically distributed to keep manual efforts and overhead low. Enter <code>renovate</code>: Whenever a new version of <code>@internal/package</code> is built, a pipeline running <code>renovate</code> is triggered to update all consumers.</p>
<p>In short I'm aiming to get these changes from <code>renovate</code>:</p>
<pre class="language-diff"><code class="language-diff"># package.json
<span class="token deleted-sign deleted"><span class="token prefix deleted">-</span><span class="token line"> "@internal/packageA": "~1.3.3",
</span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> "@internal/packageA": "~1.3.4",
</span></span>
# package-lock.json
# "node_modules/@internal/packageA": {
<span class="token deleted-sign deleted"><span class="token prefix deleted">-</span><span class="token line"> "version": "1.3.3",
</span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> "version": "1.3.4",
</span></span></code></pre>
<p>With the base recommended rules from <code>renovate</code>, it would ignore an update on the <code>1.3.x</code> branch and directly update to the latest <code>1.5.x</code> version. So I dove deeper into the options how to solve this with <code>renovate</code>.</p>
<h2><code>rangeStrategy: 'in-range-only'</code> is not sufficient</h2>
<p>The config parameter <a href="https://docs.renovatebot.com/configuration-options/#rangestrategy"><code>rangeStrategy</code></a> does offer a way to handle only in-range updates: <code>in-range-only</code>. This will update the package in the <code>package-lock.json</code> only.</p>
<p>However this is not ideal in my use-case because some of the consuming packages themselves are a dependency in another project. So this update might get lost as only the data from the <code>package.json</code> is used to resolve the appropriate versions.</p>
<h2>Solution: Individual rules with <code>matchCurrentValue</code></h2>
<p>So I needed to go a level deeper and trigger individual update rules based on the currently tracked version. This can be done with <code>matchCurrentValue</code> which contains the actual string from the <code>package.json</code>.</p>
<p>Here a full example to have patch updates for tilde-ranges, and minor and patch updates for caret-ranges:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// `config.js` for a self-hosted renovate setup to only update the `@internal/package`</span>
<span class="token comment">// triggered from pipelines whenever a new version of `@internal/package` has been published</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token comment">// ... standalone config</span>
<span class="token literal-property property">repositories</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token comment">// ... repository config</span>
<span class="token literal-property property">enabledManagers</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'npm'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token literal-property property">major</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">enabled</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">minor</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">enabled</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">patch</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">enabled</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">rangeStrategy</span><span class="token operator">:</span> <span class="token string">'bump'</span><span class="token punctuation">,</span> <span class="token comment">// ensures that entries in the `package.json` is being updated</span>
<span class="token literal-property property">separateMinorPatch</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// otherwise renovate would not track updates to patch versions, </span>
<span class="token comment">// if also a new minor exists</span>
<span class="token literal-property property">packageRules</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token comment">// disable all dependencies</span>
<span class="token literal-property property">enabled</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token literal-property property">matchDepTypes</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'devDependencies'</span><span class="token punctuation">,</span> <span class="token string">'dependencies'</span><span class="token punctuation">,</span> <span class="token string">'peerDependencies'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token literal-property property">matchPackagePatterns</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'*'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token literal-property property">matchUpdateTypes</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'minor'</span><span class="token punctuation">,</span> <span class="token string">'patch'</span><span class="token punctuation">,</span> <span class="token string">'bump'</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token comment">// Allow minor and patch updates for caret-ranges, e.g. `^1.3.0`</span>
<span class="token literal-property property">enabled</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token literal-property property">matchDepTypes</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'devDependencies'</span><span class="token punctuation">,</span> <span class="token string">'dependencies'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token literal-property property">matchPackageNames</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'@internal/package'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token literal-property property">matchUpdateTypes</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'minor'</span><span class="token punctuation">,</span> <span class="token string">'patch'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token comment">// RegExp to cover all version strings starting with a caret</span>
<span class="token literal-property property">matchCurrentValue</span><span class="token operator">:</span> <span class="token string">"/^\\^/"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token comment">// Allow patch updates for tilde-ranges, e.g. `~1.2.0`</span>
<span class="token literal-property property">enabled</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token literal-property property">matchDepTypes</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'devDependencies'</span><span class="token punctuation">,</span> <span class="token string">'dependencies'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token literal-property property">matchPackageNames</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'@internal/package'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token literal-property property">matchUpdateTypes</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'patch'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token comment">// RegExp to cover all version strings starting with a tilde</span>
<span class="token literal-property property">matchCurrentValue</span><span class="token operator">:</span> <span class="token string">"/^~/"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p>With <a href="https://docs.renovatebot.com/configuration-options/#automerge"><code>automerge</code></a> enabled and <code>renovate</code> triggered once a new version of <code>@internal/package</code> is published, this removes pretty much any manual involvement. So we can rely that after a few minutes the changes will automatically land in the consuming project. đ</p>
OpenCore Legacy Patcher2023-12-05T19:05:00Zhttps://xam.io/2023/oclp/<p>Turns out <a href="https://www.apple.com/macos/sonoma/">macOS Sonoma</a> dropped the support for my 2017 MacBook Pro. Fiddling around with my setup in general, I decided to go for the upgrade nonetheless.</p>
<p>So, I gave the <a href="https://dortania.github.io/OpenCore-Legacy-Patcher/">OpenCore Legacy Patcher</a> a shot â a tool that patches macOS to work on older hardware. It worked much smoother than I expected, and I haven't noticed any issues in the past few days.</p>
Overriding macOS Diagnostics Shortcuts2020-08-30T13:00:00Zhttps://xam.io/2020/macos-diag-shortcuts/<p>This is an addition to the last post on the <a href="https://xam.io/2020/hyper-key/">Hyper Key and Shortcuts</a>. There are a few shortcuts from macOS that already make use of the Hyper Key:</p>
<ul>
<li><code>cmd + option + ctrl + shift + .</code> : Will run the system diagnostics on macOS. This will use up resources from your system and also waste a lot of disk space in <code>/var/tmp</code></li>
<li><code>cmd + option + ctrl + shift + ,</code> : Will open the most recent system diagnose in the Finder.</li>
<li><code>cmd + option + ctrl + shift + w</code> : Will run the Wi-Fi diagnostics â also using a lot of space.</li>
</ul>
<p>An <a href="https://apple.stackexchange.com/questions/59917/how-do-you-get-system-diagnostic-files-from-macos">Apple StackExchange Thread</a> helped me figure this out. Causing my manually defined shortcuts to fail inconsistently. But I wasn't able to find a way to disable this in macOS directly.</p>
<p>But I found a workaround: Assigning these key combinations to the <code>f17 - f19</code> keys. đ Those are in general not used by macOS, and so far, I didn't run into any complications.</p>
<p>Below the relevant part of my karabiner config:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"Map Command-Shift-Option-Control-Period to f19 (avoid System Diagnostics)"</span><span class="token punctuation">,</span>
<span class="token property">"manipulators"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"from"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"period"</span><span class="token punctuation">,</span>
<span class="token property">"modifiers"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"mandatory"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"left_command"</span><span class="token punctuation">,</span>
<span class="token string">"left_control"</span><span class="token punctuation">,</span>
<span class="token string">"left_option"</span><span class="token punctuation">,</span>
<span class="token string">"left_shift"</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"to"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"f19"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"basic"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"Map Command-Shift-Option-Control-Comma to f18 (avoid System Diagnostics)"</span><span class="token punctuation">,</span>
<span class="token property">"manipulators"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"from"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"comma"</span><span class="token punctuation">,</span>
<span class="token property">"modifiers"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"mandatory"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"left_command"</span><span class="token punctuation">,</span>
<span class="token string">"left_control"</span><span class="token punctuation">,</span>
<span class="token string">"left_option"</span><span class="token punctuation">,</span>
<span class="token string">"left_shift"</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"to"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"f18"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"basic"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"Map Command-Shift-Option-Control-w to f17 (avoid Wifi Diagnostics)"</span><span class="token punctuation">,</span>
<span class="token property">"manipulators"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"from"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"w"</span><span class="token punctuation">,</span>
<span class="token property">"modifiers"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"mandatory"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"left_command"</span><span class="token punctuation">,</span>
<span class="token string">"left_control"</span><span class="token punctuation">,</span>
<span class="token string">"left_option"</span><span class="token punctuation">,</span>
<span class="token string">"left_shift"</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"to"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"f17"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"basic"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
Hyper Key and Shortcuts2020-08-30T11:45:00Zhttps://xam.io/2020/hyper-key/<p>When I researched the whole keyboard topic, I quickly stumbled over <a href="https://karabiner-elements.pqrs.org/">Karabiner Elements</a>: A tool to customize the keyboard handling in macOS. I put it to use to remap the right <code>ctrl</code> key to <code>option</code> to fix that <a href="https://xam.io/2020/keyboards-layouts/">Keychron K2 flaw</a>. But looking a bit more into it, quickly led me to the idea of a <a href="https://brettterpstra.com/2017/06/15/a-hyper-key-with-karabiner-elements-full-instructions/">Hyper Key</a>: Effectively this means mapping <code>caps_lock</code> (or any other key you don't need) to simulate the hold of essentially all modifiers (<code>ctrl</code>, <code>option</code>, <code>command</code> and <code>shift</code>). Then you can use this key in all sorts of other tools to define easy to reach keyboard shortcuts without having to be a finger acrobat.</p>
<p>In my current setup I use it to move windows around, to use <code>ijkl</code> as arrow keys, to launch all kind of tools and programs. This is a summary of my current system and the most important configurations in Karabiner Elements. You can add most of these snippets to your <code>~/.config/karabiner/karabiner.json</code> under <code>$.profiles.complex_modifications.rules</code>.</p>
<h2>Caps Lock as Hyper Key</h2>
<p>I have been using <code>caps_lock</code> as <code>esc</code> ever since I got a MacBook Pro with a touchbar. First step for me was to undo this in the Keyboard settings of macOS, to have a sane default state.</p>
<p>To start off I added the default <code>Change caps_lock to command+control+option+shift</code> modifications. Additionally, I added a rule to use <code>caps_lock</code> as <code>esc</code> when it's pressed alone:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"Change caps_lock to command+control+option+shift."</span><span class="token punctuation">,</span>
<span class="token property">"manipulators"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"from"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"caps_lock"</span><span class="token punctuation">,</span>
<span class="token property">"modifiers"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"optional"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"any"</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"to"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"left_shift"</span><span class="token punctuation">,</span>
<span class="token property">"modifiers"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"left_command"</span><span class="token punctuation">,</span>
<span class="token string">"left_control"</span><span class="token punctuation">,</span>
<span class="token string">"left_option"</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"to_if_alone"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"escape"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"basic"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<h2>Use i,j,k,l as Arrow Keys</h2>
<p>Moving my hand from the home row down to the arrow keys is probably a movement I do way to often. Now I can use the</p>
<ul>
<li><code>hyper + i</code> as <code>up</code></li>
<li><code>hyper + j</code> as <code>left</code></li>
<li><code>hyper + k</code> as <code>down</code></li>
<li><code>huper + l</code> as <code>right</code></li>
</ul>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"Change hyper+jikl to arrow keys"</span><span class="token punctuation">,</span>
<span class="token property">"manipulators"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"from"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"j"</span><span class="token punctuation">,</span>
<span class="token property">"modifiers"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"mandatory"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"left_command"</span><span class="token punctuation">,</span>
<span class="token string">"left_control"</span><span class="token punctuation">,</span>
<span class="token string">"left_option"</span><span class="token punctuation">,</span>
<span class="token string">"left_shift"</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"optional"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"any"</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"to"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"left_arrow"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"basic"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token property">"from"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"k"</span><span class="token punctuation">,</span>
<span class="token property">"modifiers"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"mandatory"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"left_command"</span><span class="token punctuation">,</span>
<span class="token string">"left_control"</span><span class="token punctuation">,</span>
<span class="token string">"left_option"</span><span class="token punctuation">,</span>
<span class="token string">"left_shift"</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"optional"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"any"</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"to"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"down_arrow"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"basic"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token property">"from"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"i"</span><span class="token punctuation">,</span>
<span class="token property">"modifiers"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"mandatory"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"left_command"</span><span class="token punctuation">,</span>
<span class="token string">"left_control"</span><span class="token punctuation">,</span>
<span class="token string">"left_option"</span><span class="token punctuation">,</span>
<span class="token string">"left_shift"</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"optional"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"any"</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"to"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"up_arrow"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"basic"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token property">"from"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"l"</span><span class="token punctuation">,</span>
<span class="token property">"modifiers"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"mandatory"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"left_command"</span><span class="token punctuation">,</span>
<span class="token string">"left_control"</span><span class="token punctuation">,</span>
<span class="token string">"left_option"</span><span class="token punctuation">,</span>
<span class="token string">"left_shift"</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"optional"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"any"</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"to"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"key_code"</span><span class="token operator">:</span> <span class="token string">"right_arrow"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"basic"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<h2>Remapping my Existing Shortcuts</h2>
<p>While I have built a lot of muscle memories in the past to control all this stuff with various modifiers, the Hyper Key removes a lot of the conflicts with other programs.</p>
<p>My current config (set in the particular programs):</p>
<ul>
<li><code>hyper + up / down / left / right</code> : Moving windows to the left / right halves of my screen, and to maximize and restore the windows using <a href="https://folivora.ai/">BetterTouchTool</a>.</li>
<li><code>hyper + t</code> : Toggling the <a href="https://iterm2.com/">iTerm</a> hotkey window</li>
<li><code>hyper + m</code> : Toggling mute with <a href="https://mutify.app/">Mutify</a></li>
<li><code>hyper + .</code> : Open the <a href="https://culturedcode.com/things/">Things</a> Quick Entry window</li>
<li><code>hyper + c</code> : Open the <a href="https://www.alfredapp.com/help/features/clipboard/">Clipboard History in Alfred</a></li>
<li><code>hyper + p</code> : Open <a href="https://1password.com/">1Password</a></li>
<li><code>hyper + a</code> : Connect the Airpods with <a href="https://c-command.com/toothfairy/">Toothfairy</a></li>
</ul>
<p><strong>Update:</strong> I was running into issues with the <code>hyper + .</code> shortcut. Turns out this combination is already taken by the sysdiag of macOS. I added a <a href="https://xam.io/2020/macos-diag-shortcuts/">post on how to remap those</a>.</p>
Switching Keyboards and Layouts2020-08-22T11:30:00Zhttps://xam.io/2020/keyboards-layouts/<p>For the past nine years, I have been using the various versions of Apple's external Magic Keyboard with a German keyboard layout and was pretty content with them. Even on the MacBook Pro I didn't experience the Butterfly keyboard as bad as its general reputation.</p>
<p>But being as easily tempted by new gadgets as I am, one topic got me curious over the past few months: Mechanical Keyboards. To test out these water, I gave in to this craving and bought a <a href="https://www.keychron.com/products/keychron-k2-wireless-mechanical-keyboard">Keychron K2</a>. And while I was at it, I also opted for the US layout to give it a try.</p>
<p>These are my first impressions three weeks in.</p>
<h2>Keychron K2 - A Quick Review</h2>
<img src="https://xam.io/uploads/2020/keychron-k2.webp" width="100%" loading="lazy" alt="" />
<p>For a lack of proper comparison I will keep my thoughts on the keyboard short. My main reasoning for this model was the relatively standard layout, the good macOS support with bluetooth and also a bit the price.</p>
<ul>
<li>It's a very different typing experience. I tasted various switches briefly and settled on the Gateron Brown ones. I'm still in the learning phase to figure out how much force I must apply.</li>
<li>Bluetooth and battery are nice. No drops in connection; switching devices works well; only one recharge since I got it.</li>
<li>It seems odd that they put a <code>ctrl</code> key to the right of the spacebar instead of an <code>option</code> key.</li>
</ul>
<p>I resolved the last point by remapping <code>right_ctrl</code> to <code>right_option</code> using <a href="https://karabiner-elements.pqrs.org/">Karabiner Elements</a> â A nifty tool to do all sorts of funny things with keyboards.</p>
<h2>Switching to the US-Layout</h2>
<p>The switch from a German ISO layout to the US ANSI layout is a much harder topic. I'm currently probably at around 75% in terms of accuracy and speed. In particular the special characters still give me quite some trouble. That being said, I see now where a lot of the shortcuts in developer tools and the syntax of some language constructs are coming from.</p>
<p>A very specific topic: Umlauts. I'm still writing a lot in German as well, so the absence of Umlauts is actually a handicap. Long-pressing the bare characters until macOS' character picker appears is a no-go. Looking at alternative layouts, I started out with using the <a href="https://eurkey.steffen.bruentjen.eu/">EurKey</a> layout which adds all sorts of special characters with the <code>option</code> and <code>option+shift</code> modifiers. However, it broke all the regular characters usually reached with these modifiers, in particular I noticed it on the <code>en</code> and <code>em</code> dashes missing.</p>
<p>In the end I settled on the <a href="https://hci.rwth-aachen.de/usgermankeyboard">USGerman Keyboard Layout</a> which only adds the umlauts. It's a huge timesaver and I cannot recommend it enough to anybody writing in English and German.</p>
<h2>A First Summary</h2>
<p>Using this mechanical keyboard and switching to the US layout surely won't make me a better typist all by itself. So, I'm not sure if there are enough benefits warranting all of this but it is a nice experiment and the novelty makes it pretty exciting for now.</p>
<p>And the overall topic already led me down the next rabbit hole: Setting the caps lock as Hyper Key and configuring all sorts of shortcuts. But that's for another post.</p>
Node.js Best Practices2020-08-17T17:20:00Zhttps://xam.io/2020/node-best-practices/<p>I only learned about it on <a href="https://changelog.com/jsparty/139">the latest episode</a> of the JS Party podcast: The <a href="https://github.com/goldbergyoni/nodebestpractices">Node.js Best Practices</a> repository is a compilation of â you guessed it â best practices for developing services in Node.js. I'm happy this exists. It codifies a lot of the "feelings" and opinions I have on what is good code and which directions a code base should go. There are also quite a few things that I didn't bother so far. Looking forward to bring some new things into projects I'm working on.</p>
Increasing the Fun in Video Calls2020-07-28T19:00:00Zhttps://xam.io/2020/fun-video-calls/<p>Working from home and spending a lot of time on video calls, got me wondering whether I could set up a system to have some sort of videoboard: Being able to have reaction-gifs or small clips playing on my video feed.</p>
<video src="https://xam.io/uploads/2020/obs-zoom.mp4" autoplay="" loop="" loading="lazy" style="margin: 1rem auto; display: block;">
<p>This is my quick documentation on how I set everything up (targeted to macOS, but should work similarly on other platforms).</p>
<h2>What you'll need</h2>
<ul>
<li><a href="https://obsproject.com/">OBS</a></li>
<li><a href="https://github.com/johnboiles/obs-mac-virtualcam">obs-mac-virtualcam</a></li>
<li><a href="https://obsproject.com/forum/resources/advanced-scene-switcher.395/">Advanced Scene Switcher</a></li>
<li><a href="https://github.com/ExistentialAudio/BlackHole">BlackHole</a></li>
</ul>
<h2>OBS as Video Source</h2>
<p><a href="https://obsproject.com/">OBS</a> is mostly known in the context of streaming your gaming or coding. But only very recently the <a href="https://github.com/johnboiles/obs-mac-virtualcam">obs-mac-virtualcam</a> became stable enough so you can use the output of OBS as a webcam in other programs <sup class="footnote-ref"><a href="https://xam.io/2020/fun-video-calls/#fn1" id="fnref1">[1]</a></sup>.</p>
<p>So with OBS and the virtualcam plugin installed, you can start OBS and already add your webcam (<code>Video Capture Device</code>). Then select <code>Tools > Start Virtual Camera</code> from the menubar and you can already use it in your next video call.</p>
<h2>Adding Clips as Scenes</h2>
<p>So go ahead and download your favourite short video clips from Youtube (easiest with <a href="https://youtube-dl.org/">youtube-dl</a>) and drop them into new scenes in OBS (you might have to resize them to fill the whole screen).</p>
<p>At this point you can already cut between the different clips and it is shown on your video feed. However you always have to manually switch back to camera feed. Advanced Scene Switcher to the rescue: Under <code>Media</code> you can create automations to switch back to the camera whenever a media file has ended.</p>
<p><img src="https://xam.io/uploads/2020/obs-scene-switcher.webp" alt="" /></p>
<h2>Audio</h2>
<p>So the video part is ready to go, but how to rick-roll your colleagues without sound? <a href="https://github.com/ExistentialAudio/BlackHole">BlackHole</a> allows us to pipe sound from one program to another. So we want to have OBS output all necessary audio signals to BlackHole and then use BlackHole as audio input in your video conferencing program.</p>
<p>We're going to use the Monitoring functionality and set <code>BlackHole 16ch</code> as <code>Settings > Audio > Advanced > Monitoring Device</code>. Additionally we must also output the microphone to the monitoring device.</p>
<p><img src="https://xam.io/uploads/2020/obs-audio.webp" alt="" /></p>
<p>All that's left to do: Use the <code>BlackHole</code> audio device as microphone input in your video tool. đ</p>
<hr />
<h2>Notes</h2>
<ul>
<li>Sometimes I had only static noise on the <code>BlackHole</code> audio device and had to restart my computer. So if you plan to use this setup be sure to give a quick test-run before you annoy everyone on the call.</li>
<li>You can also set hotkeys to switch to scenes â makes it much more comfortable to switch between scenes.</li>
</ul>
<hr class="footnotes-sep" />
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p><a href="https://github.com/johnboiles/obs-mac-virtualcam/wiki/Compatibility">Support is still limited.</a> But for Zoom or using the Browser is fine in most cases. <a href="https://xam.io/2020/fun-video-calls/#fnref1" class="footnote-backref">âŠī¸</a></p>
</li>
</ol>
</section>
</video>macOS Wifi Woes2020-04-28T15:00:00Zhttps://xam.io/2020/macos-wifi/<p>I spent a large part of the past year being annoyed at the Wifi connection of my MacBook: Every once in a while it would clearly drop all traffic for a few seconds while reporting that everything is fine. This is particular annoying when being on audio or video calls. It occurred once or twice a week but I couldn't reproduce it at will. So I set out on a pretty long troubleshooting journey that got me a few times on the brink of reinstalling macOS from a clean slate. In the end the fix turned out pretty straightforward and by writing this post, I hope to remember it the next time macOS has such hiccups.</p>
<h2>The Solution</h2>
<ol>
<li>Open Network Preferences</li>
<li>Create a new Network Location and switch to it</li>
<li>Reboot</li>
<li>Connect to your network</li>
<li>...</li>
<li>Profit!</li>
</ol>
<p>Having done this a few month ago I never ran into the wifi drops ever since. đ¤</p>
<img src="https://xam.io/uploads/2020/network.webp" width="100%" alt="" loading="lazy" />
<h2>Other failed attempts</h2>
<p>As mentioned, it was a pretty long time I endured this situation, but it wasn't for a lack of trying to fix it. Here a list of things that didn't work for me:</p>
<ul>
<li><a href="https://support.apple.com/en-us/HT204063"><strong>NVRAM / PRAM reset:</strong></a> All time classic when dealing with issues on a Mac, sadly didn't bring any salvation this time.</li>
<li><strong>Changing Wifi Routers:</strong> Both at work and at home I was connected to Ubiquiti access points, so I assumed that maybe there is something afoul with this connection. But nobody else at work was affected, nor did changing back to an older Wifi-Router at home help.(And yes, I even fiddled with the MTU sizes, which brought back memories from LAN parties in the early 2000s)</li>
<li><a href="https://osxdaily.com/2012/11/30/resolving-stubborn-wi-fi-connection-problems-in-mac-os-x/"><strong>Removing the plist files:</strong></a> Nope, no improvements either.</li>
<li><a href="https://support.apple.com/guide/mac-help/use-wireless-diagnostics-mchlf4de377f/mac"><strong>Running Wifi Diagnostics:</strong></a> Even when I was lucky enough to catch a dropout when running the wireless diagnostics, it wouldn't list any issues.</li>
</ul>
Working from Home: Audio Edition2020-04-17T16:31:00Zhttps://xam.io/2020/wfh-audio/<p>Spending a good amount of my days in calls nowadays, audio quality is quite important. I have a plethora of headphones so I can comfortably listen to others on calls, but figuring out how to sound my own best is a bit more tricky and pretty much an ongoing process. Currently I settled on the microphone of the external webcam together with <a href="https://krisp.ai/">krisp.ai</a>.</p>
<hr />
<p>I have been content with the quality of using either the <a href="https://xam.io/2017/qc-35-ii-vs-1000xm2/">Bose QC35</a> or the AirPods via Bluetooth for calls so far. But when we as a team at work decided to gather in a permanently open <a href="https://www.mumble.info/">Mumble room</a><sup class="footnote-ref"><a href="https://xam.io/2020/wfh-audio/#fn1" id="fnref1">[1]</a></sup> things got a bit more tricky: Having the microphone channel open, everything switches to the low-quality low-latency SCO codec. This makes listening to music and any other media rather unpleasant.</p>
<p>So I started fiddling with other options to split microphone input and audio output. The microphone on the MacBook quickly showed that it's too much dependent on my relative position to it and even worse that you could hear the fans spinning up. So I was left with the external webcam I use, a <a href="https://www.logitech.com/en-gb/product/hd-pro-webcam-c920">Logitech c920</a>. While I would say the sound itself is okayish it had a pretty bad echo. A colleague described it as preaching in a church. Having read about <a href="https://ref.krisp.ai/u/u9387d16b0?utm_source=refprogram&utm_campaign=153867&locale=en-DE">krisp.ai</a><sup class="footnote-ref"><a href="https://xam.io/2020/wfh-audio/#fn2" id="fnref2">[2]</a></sup> earlier I gave it a shot, and lo and behold I'm impressed: It completely removes the echo and also filters out much of the ambient noise from traffic, most typing, and similar things. So I don't have to do the mute-unmute-dance after every sentence anymore.</p>
<p>Here are two audio samples:</p>
<ul>
<li>
Without krisp.ai<br />
<audio controls=""><source src="https://xam.io/uploads/2020/c920-without-krisp.m4a" /></audio>
</li>
<li>
With krisp.ai active<br />
<audio controls=""><source src="https://xam.io/uploads/2020/c920-with-krisp.m4a" /></audio>
</li>
</ul>
<p>For now I'm quite happy that I can listen to music and chime in on Mumble (and Zooms) without having to reconfigure everything all the time. Nonetheless I'm already prying on a dedicated microphone as it looks like the work-from-home situation will continue for months to come.</p>
<hr class="footnotes-sep" />
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>Most of the time everybody is muted. But it is nice to have a direct chat when something comes up. <a href="https://xam.io/2020/wfh-audio/#fnref1" class="footnote-backref">âŠī¸</a></p>
</li>
<li id="fn2" class="footnote-item"><p>Referral link to get a month at krisp.ai for free <a href="https://xam.io/2020/wfh-audio/#fnref2" class="footnote-backref">âŠī¸</a></p>
</li>
</ol>
</section>
Finicky - Always open the right browser2020-03-23T21:27:00Zhttps://xam.io/2020/finicky/<p>Thanks the <a href="https://en.wikipedia.org/wiki/2019%E2%80%9320_coronavirus_pandemic">ongoing pandemic</a>, I currently work from home and spend much more time in video calls. We use a variety of systems to do that: <a href="https://slack.com/">Slack</a>, <a href="https://zoom.us/">Zoom</a>, <a href="https://meet.google.com/">Google Meet / Hangout</a> and <a href="https://jitsi.org/">Jitsi</a>. Especially the last two systems run exclusively in the browser. And while I am a big fan of Firefox and use its <a href="https://www.mozilla.org/en-US/firefox/developer/">Developer Edition</a> as my main browser, video calls appear to run smoother in Chrome.</p>
<p>Until today I would most often follow links to Hangouts or Jitsi to open in Firefox only to copy over the URL to Chrome. However I wondered whether there is a better way to do this â that's how I stumbled upon <a href="https://github.com/johnste/finicky">Finicky</a>: A small macOS utility to set as default browser and that can be scripted for which URLs to open in what browser.</p>
<p>So no matter where I click on a Jitsi or Hangouts link â it will open in Chrome but everything else will open in Firefox.</p>
<p>My current configuration:</p>
<pre class="language-js"><code class="language-js">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">defaultBrowser</span><span class="token operator">:</span> <span class="token string">"Firefox Developer Edition"</span><span class="token punctuation">,</span>
<span class="token literal-property property">handlers</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token literal-property property">match</span><span class="token operator">:</span> finicky<span class="token punctuation">.</span><span class="token function">matchHostnames</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"meet.jit.si"</span><span class="token punctuation">,</span> <span class="token string">"meet.google.com"</span><span class="token punctuation">,</span> <span class="token string">"hangouts.google.com"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token literal-property property">browser</span><span class="token operator">:</span> <span class="token string">"Google Chrome"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token literal-property property">match</span><span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">zoom.us\/j\/</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>
<span class="token literal-property property">browser</span><span class="token operator">:</span> <span class="token string">"us.zoom.xos"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<ul>
<li><em>Update 2020-03-28:</em> Added a config to open Zoom links directly in the app.</li>
</ul>