<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yohanes Bandung Bondowoso</title>
    <description>The latest articles on DEV Community by Yohanes Bandung Bondowoso (@ybbond).</description>
    <link>https://dev.to/ybbond</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F154650%2Fdc29aa4c-e698-4fa7-a96b-174990295eb0.jpeg</url>
      <title>DEV Community: Yohanes Bandung Bondowoso</title>
      <link>https://dev.to/ybbond</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ybbond"/>
    <language>en</language>
    <item>
      <title>Non Null Type Inference After Null Checking in Flutter</title>
      <dc:creator>Yohanes Bandung Bondowoso</dc:creator>
      <pubDate>Mon, 08 Nov 2021 14:12:18 +0000</pubDate>
      <link>https://dev.to/ybbond/non-null-type-inference-after-null-checking-in-flutter-41po</link>
      <guid>https://dev.to/ybbond/non-null-type-inference-after-null-checking-in-flutter-41po</guid>
      <description>&lt;p&gt;A shorts.&lt;/p&gt;

&lt;p&gt;Suppose I make this cool simple widget with sound null safety:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/material.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CoolWidget&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CoolWidget&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CoolWidget&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="o"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Y U NO GIVE TEXT?'&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flutter will get angry and &lt;strong&gt;summon this error log&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Error: The argument &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;'String?'&lt;/span&gt; can&lt;span class="s1"&gt;'t be assigned to the parameter type '&lt;/span&gt;String&lt;span class="s1"&gt;' because '&lt;/span&gt;String?&lt;span class="s1"&gt;' is nullable and '&lt;/span&gt;String&lt;span class="s1"&gt;' isn'&lt;/span&gt;t.

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;text &lt;span class="o"&gt;!=&lt;/span&gt; null&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;Text&lt;span class="o"&gt;(&lt;/span&gt;text&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                              ^     
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is silly that Flutter cannot dictate from the null check &lt;code&gt;text != null&lt;/code&gt; not a moment ago, and infer that the &lt;code&gt;text&lt;/code&gt; is in fact non-nullable. There is only one possible control flow from that point on!&lt;/p&gt;

&lt;p&gt;There is &lt;strong&gt;&lt;em&gt;bang operator&lt;/em&gt;&lt;/strong&gt; (&lt;code&gt;!&lt;/code&gt; operator) to tell Flutter: “Yeah, I know it is typed as nullable, but I am super duper sure it will not be a null!”&lt;/p&gt;

&lt;p&gt;Which sounds dangerous, no? Code changes, maintainer changes, the world is changing. There is a great chance that sometime in the future, a refactor done and the maintainer forget to deal with the &lt;em&gt;bang operator&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;After browsing the internet for a bit, I found that there &lt;strong&gt;was&lt;/strong&gt; a mention in the Dart docs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The analyzer can’t model the flow of your whole application, so it can’t predict the values of global variables or class fields.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;which used to be &lt;a href="https://dart.dev/null-safety"&gt;in this page of docs&lt;/a&gt;, but currently not exists.&lt;/p&gt;

&lt;p&gt;From that documentation text, implied that non-null inference can be done in a local scope.So if we define new local variable and do null check on that var, Flutter will gladly say: “OK homie, peace out ✌️”.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/lib/main.dart b/lib/main.dart
index 96969696..69696969 100644
&lt;/span&gt;&lt;span class="gd"&gt;--- a/lib/main.dart
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/lib/main.dart
&lt;/span&gt;&lt;span class="p"&gt;@@ -13,7 +13,8 @@&lt;/span&gt; class Cool Widget extends StatelessWidget {

  @override 
  Widget build(BuildContext build) {
&lt;span class="gd"&gt;-   if (text != null) return Text(text);
&lt;/span&gt;&lt;span class="gi"&gt;+   final String? newText = text;
+   if (newText != null) return Text(newText);
&lt;/span&gt;
    return Text('Y U NO GIVE TEXT?');
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Final code looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/material.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CoolWidget&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CoolWidget&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CoolWidget&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="o"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;newText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newText&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newText&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Y U NO GIVE TEXT?'&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other Example
&lt;/h2&gt;

&lt;p&gt;It’s not just variable (re)declaration, but &lt;strong&gt;passing parameter to a method also works&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, this will summon error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/material.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CoolWidget&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CoolWidget&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CoolWidget&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="o"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_buildBuildBuild&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;_buildBuildBuild&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Y U NO GIVE TEXT?'&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;    
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might have guessed that the solution is to pass the parameter to the private method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/lib/main.dart b/lib/main.dart
index 96969696..69696969 100644
&lt;/span&gt;&lt;span class="gd"&gt;--- a/lib/main.dart
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/lib/main.dart
&lt;/span&gt;&lt;span class="p"&gt;@@ -19,8 +19,8 @@&lt;/span&gt; Widget build(BuildContext build) {
     return _buildBuildBuild();
   }

-   Widget _buildBuildBuild() {
&lt;span class="gd"&gt;-     if (text != null) return Text(text);
&lt;/span&gt;&lt;span class="gi"&gt;+   Widget _buildBuildBuild({String? newText}) {
+     if (newText != null) return Text(newText);
&lt;/span&gt;
      return Text('Y U NO GIVE TEXT?'); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I personally like this approach, rather than using the &lt;em&gt;bang operator&lt;/em&gt; (&lt;code&gt;!&lt;/code&gt; operator).&lt;/p&gt;

&lt;p&gt;Will there be performance impact because we declare some new variables? There might be.&lt;/p&gt;

&lt;p&gt;Is passing method parameter counts as declaring new variable? I honestly don’t know. Do tell me if you have insights about this 😃&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dart-lang/language/issues/1472"&gt;https://github.com/dart-lang/language/issues/1472&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://groups.google.com/a/dartlang.org/g/misc/c/xYKOXNMiFpI"&gt;https://groups.google.com/a/dartlang.org/g/misc/c/xYKOXNMiFpI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/65456958/dart-null-safety-doesnt-work-with-class-fields/65457221#65457221"&gt;https://stackoverflow.com/questions/65456958/dart-null-safety-doesnt-work-with-class-fields/65457221#65457221&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>programming</category>
    </item>
    <item>
      <title>Some Apple Scripts for Safari</title>
      <dc:creator>Yohanes Bandung Bondowoso</dc:creator>
      <pubDate>Sun, 23 Aug 2020 12:48:48 +0000</pubDate>
      <link>https://dev.to/ybbond/some-apple-scripts-for-safari-o22</link>
      <guid>https://dev.to/ybbond/some-apple-scripts-for-safari-o22</guid>
      <description>&lt;p&gt;Safari Technology Preview is my primary web browser when I am not developing website. It is the beta equivalent for Safari that features the latest updates, including the «Tracker Blocker» that will be released to public on macOS Big Sur.&lt;/p&gt;

&lt;p&gt;One of the main losses is the lack of good browser extension to simulate Vim key bindings. &lt;a href="https://github.com/televator-apps/vimari"&gt;Vimari&lt;/a&gt; is okay, but it doesn’t work correctly on GitHub. The other losses are the lack of accidental quit prevention and alternative key binding to change tab.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prevent accidental quit
&lt;/h2&gt;

&lt;p&gt;Firefox and Chrome has option to warn us if we press &lt;code&gt;⌘ + Q&lt;/code&gt; accidentally. Safari doesn’t provide this option. This often happen when I want to close a tab with &lt;code&gt;⌘ + W&lt;/code&gt;, but my clumsy ring finger misses that &lt;code&gt;W&lt;/code&gt;. Previously, I use &lt;code&gt;App Shortcuts&lt;/code&gt; modifier (System Preferences → Keyboard → Shortcuts → App Shortcuts) and assign it to &lt;code&gt;⌘ + ⌥ + Q&lt;/code&gt;. It works, but I am curious whether there’s another way.&lt;/p&gt;

&lt;p&gt;I browsed the internet, and found that &lt;strong&gt;John Gruber&lt;/strong&gt; already shared the solution for this issue. You can read it on this post: &lt;a href="https://daringfireball.net/2020/01/quit_confirmation_for_safari_on_macos"&gt;Quit Confirmation for Safari on MacOS&lt;/a&gt; via DaringFireball.net.&lt;/p&gt;

&lt;p&gt;For your convenience, I will share the slightly modified script here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight applescript"&gt;&lt;code&gt;&lt;span class="k"&gt;tell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;application&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Safari Technology Preview"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;windowCount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;windows&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tabCount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="k"&gt;repeat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;currWindow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;every&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tabCount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tabCount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tabs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;currWindow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;repeat&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="c1"&gt;-- Make a string like "1 window containing 3 tabs."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;windowCount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;windowCount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" window containing "&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as &lt;/span&gt;&lt;span class="nc"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;windowCount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" windows containing "&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as &lt;/span&gt;&lt;span class="nc"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tabCount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tabCount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" tab."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as &lt;/span&gt;&lt;span class="nc"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tabCount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" tabs."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as &lt;/span&gt;&lt;span class="nc"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nb"&gt;display alert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;¬
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"Are you sure you want to quit Safari?"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;¬
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;buttons&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Cancel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Quit"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;¬
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;giving&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;after&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;button returned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Quit"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;quit&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;tell&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that you might want to change from &lt;code&gt;Safari Technology Preview&lt;/code&gt; to only &lt;code&gt;Safari&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You’d want to put the script to &lt;code&gt;Script Editor.app&lt;/code&gt;, and —as suggested by John, use &lt;a href="https://red-sweater.com/fastscripts/"&gt;FastScripts&lt;/a&gt;. FastScripts is awesome! Credits to red sweater. What you should do is move the scripts generated by Script Editor to FastScripts’ folder for Safari and assign your preferred key bindings for it. Obviously you’d want to assign it to &lt;code&gt;⌘ + Q&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;More info on FastScripts, you can check &lt;a href="https://help.red-sweater.com/fastscripts/faq/"&gt;their FAQ here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Because we’ve covered the issue about quitting, now moving to the next issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternative key bindings to change tab
&lt;/h2&gt;

&lt;p&gt;The default key bindings to change tab for Safari is &lt;code&gt;⌃ + ⇥&lt;/code&gt; for Next Tab, and &lt;code&gt;⌃ + ⇧ + ⇥&lt;/code&gt; for Previous Tab. I’d like to add the already familiar key bindings that exist on Firefox and Chrome, that is &lt;code&gt;⌘ + ⌥ + ←&lt;/code&gt; for Previous Tab and &lt;code&gt;⌘ + ⌥ + →&lt;/code&gt; for Next Tab.&lt;/p&gt;

&lt;p&gt;I tried using Mac’s built in &lt;code&gt;App Shortcuts&lt;/code&gt; modifier (System Preferences → Keyboard → Shortcuts → App Shortcuts) to modify it, but I’d lose the original key bindings. I want them both to coexist.&lt;/p&gt;

&lt;p&gt;My solution is by using the Apple Script and FastScripts too.&lt;/p&gt;

&lt;p&gt;Hereby the script to change tab to Next Tab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight applescript"&gt;&lt;code&gt;&lt;span class="k"&gt;tell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;application&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Safari Technology Preview"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="k"&gt;tell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;myTab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;goodtab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;myTab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;integer&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;goodtab&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nb"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;error&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;tell&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;tell&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the script to change tab to Previous Tab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight applescript"&gt;&lt;code&gt;&lt;span class="k"&gt;tell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;application&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Safari Technology Preview"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="k"&gt;tell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;myTab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;goodtab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;myTab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;integer&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;goodtab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;tell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;application&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Safari Technology Preview"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lastTab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tabs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lastTab&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;goodtab&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;tell&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;tell&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’d want to change the &lt;code&gt;Safari Technology Preview&lt;/code&gt; too. The scripts are straight forward, one of the advantages of Apple Script’s strange syntax. Let me explain a bit about the &lt;code&gt;try ... on error&lt;/code&gt; and &lt;code&gt;if ... else&lt;/code&gt; part.&lt;/p&gt;

&lt;p&gt;When you use the Next Tab script and reach the last tab, you’d encounter error because the last tab + 1 is nowhere to be found. In that case, we move to the first tab. That’s the expected behavior too, because we’d want the key binding to keep looping on the available tabs.&lt;/p&gt;

&lt;p&gt;For the Previous Tab script, it gets a little complicated, because there is no property or method to get the length of available tabs that I know of. So I ask (&lt;code&gt;tell&lt;/code&gt;) Safari to define a &lt;code&gt;lastTab&lt;/code&gt; variable with the number of available tabs on the active Safari window as its value.&lt;/p&gt;

&lt;p&gt;Prior to this day, I never thought that I’d use Apple Scripts and these kind of macOS utility tools helps my day this much. I read Mac Power User, listen to Rosemary Orchard’s podcasts &lt;a href="https://nestedfolderspodcast.com"&gt;about productivity&lt;/a&gt; and &lt;a href="https://www.relay.fm/automators"&gt;automating some tasks&lt;/a&gt;, and others. Now I know how awesome the previously overlooked tools is.&lt;/p&gt;

</description>
      <category>tools</category>
      <category>apple</category>
    </item>
    <item>
      <title>Inclusive Content Sharing</title>
      <dc:creator>Yohanes Bandung Bondowoso</dc:creator>
      <pubDate>Sun, 28 Jun 2020 09:44:38 +0000</pubDate>
      <link>https://dev.to/ybbond/inclusive-content-sharing-ci1</link>
      <guid>https://dev.to/ybbond/inclusive-content-sharing-ci1</guid>
      <description>&lt;p&gt;This is a complicated world. Each individuals are different, and there are many factors to consider while interacting with others. People who don't eat pork, or won't eat meat. People who fond of talking about ghost, and the ones easily scared. You must leave the toilet seat closed in some house, in some others opened.&lt;/p&gt;

&lt;p&gt;Internet adds to that complexity. For instance you share a cute picture of a snake. There may be someone having &lt;em&gt;Ophidiophobia&lt;/em&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt; who will block you. Not because they hate you, but only to get rid of that "horrible picture" of a scary creature.&lt;/p&gt;

&lt;h2&gt;
  
  
  On Inclusivity
&lt;/h2&gt;

&lt;p&gt;The awareness of inclusivity had spread wider day by day. There is &lt;strong&gt;Inclusive Design&lt;/strong&gt;, which concepts encourage you to think about &lt;strong&gt;diversity&lt;/strong&gt; and &lt;strong&gt;accessibility&lt;/strong&gt; when building products.&lt;/p&gt;

&lt;p&gt;Example of diversity: provide more gender dropdown choice knowing some people won't feel comfortable sharing their gender.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TrkRI7lG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-06-inclusive/gender-selection.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TrkRI7lG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-06-inclusive/gender-selection.png" alt="Image courtesy of post from Sarah Dopp"&gt;&lt;/a&gt;&lt;/p&gt;
Image courtesy of &lt;a rel="nofollow noreferrer" href="http://www.sarahdopp.com/blog/2010/designing-a-better-drop-down-menu-for-gender/"&gt;post from Sarah Dopp&lt;/a&gt;



&lt;p&gt;Example of accessibility: make your website content recognizable by text-to-speech software for those having difficulties reading. Other example of accessibility: provide certain color scheme on video game's User Interface, for those having difficulties differentiating certain colors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content Sharing
&lt;/h2&gt;

&lt;p&gt;Back to the topic of content sharing. I gave you example about sharing picture of animal that some people may find disturbing. That is one thing.&lt;/p&gt;

&lt;p&gt;Other thing is to think about individuals that do extra effort to conceal their &lt;strong&gt;privacy&lt;/strong&gt;, even from Google&lt;sup id="fnref2"&gt;2&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;What is it about Google? Well, some people believe that accessing Google's domains or endpoints may give off their privacy to that company. The obvious example is using Google Analytics for any website&lt;sup id="fnref3"&gt;3&lt;/sup&gt;. That practice may give off visitors' data to Google. Other example is using Google Fonts, or embedding Youtube videos. Extreme example is linking a text to Youtube, like how I did in some of my posts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k0oKBDn---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-06-inclusive/2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k0oKBDn---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-06-inclusive/2.png" alt="Previously, I share Youtube links too" title="Previously, I share Youtube links too"&gt;&lt;/a&gt;&lt;/p&gt;
Previously, I share Youtube links too



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r_DYx7QY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://ybbond.dev/posts/2020-06-inclusive/youtube-invidious.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r_DYx7QY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://ybbond.dev/posts/2020-06-inclusive/youtube-invidious.gif" alt="Changed those to Invidious" title="Changed those to Invidious"&gt;&lt;/a&gt;&lt;/p&gt;
Changed those to Invidious



&lt;p&gt;I replaced the links to &lt;a href="https://invidio.us"&gt;Invidio.us&lt;/a&gt; though. Hope that helps.&lt;/p&gt;

&lt;p&gt;So we talked about disturbing content and privacy. I am also thinking about &lt;strong&gt;content availability&lt;/strong&gt;. The example is sharing great content that some people may not be able to retrieve, because it is hidden behind paywall. The example is some content on Medium, or NYTimes.&lt;/p&gt;

&lt;p&gt;I often encounter this on Hacker News, where the linked content is a paywalled post on Medium, or NYTimes. There is a work-around for this, and I know maybe that great content only available on that certain platform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6Odsul7M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://ybbond.dev/posts/2020-06-inclusive/medium-paywall.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6Odsul7M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://ybbond.dev/posts/2020-06-inclusive/medium-paywall.gif" alt="GIF animation of Medium Paywall, also an irony" title="GIF animation of Medium Paywall, also an irony"&gt;&lt;/a&gt;&lt;/p&gt;
GIF animation of Medium Paywall, also an irony



&lt;p&gt;I train myself to take some time searching whether that post shared elsewhere and share the alternative link instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;You cannot please everyone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The topics I've written are not all. Others may think other factors they find necessary to consider before writing post or sharing links on the internet.&lt;/p&gt;

&lt;p&gt;Easier written than said, even more done.&lt;/p&gt;

&lt;p&gt;Sometimes I forget about some factors, and I am willing to be stand corrected for that. Thank you for all of you taking your precious time for reminding me. Truly appreciate your good intention.&lt;/p&gt;

&lt;p&gt;As a closing statement, this is a complicated world. I can be a simpleton and ignoring all those complexity, but I choose the option I think making this world a more pleasant place to live. For me, and others.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Fear of snake. Even they may feel the fear only by reading the word of that animal. Sorry then :( ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://alirezahayati.com/2020/06/dont-trust-privacy-options/"&gt;Don't trust privacy options&lt;/a&gt; → &lt;strong&gt;Ali Reza Hayati&lt;/strong&gt; boldly written about how some companies may use your private informations for scary things. Not now, maybe, but might be in the future. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;a href="https://vincent.bernat.ch/en/blog/2018-more-privacy-blog"&gt;A more privacy-friendly blog&lt;/a&gt; → This is a great post by &lt;strong&gt;Vincent Bernat&lt;/strong&gt; listing privacy friendly alternatives for tools oft used on website. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>privacy</category>
      <category>sharing</category>
      <category>inclusive</category>
    </item>
    <item>
      <title>Org-mode with Org-roam</title>
      <dc:creator>Yohanes Bandung Bondowoso</dc:creator>
      <pubDate>Sat, 27 Jun 2020 16:05:28 +0000</pubDate>
      <link>https://dev.to/ybbond/org-mode-with-org-roam-2ng0</link>
      <guid>https://dev.to/ybbond/org-mode-with-org-roam-2ng0</guid>
      <description>&lt;p&gt;On a late night some twenty or more days ago, my friend &lt;a href="https://twitter.com/broerjuang"&gt;Juang&lt;/a&gt; contacted me on Discord.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On Wed, Jun 03, 2020 at 01.20 AM &lt;a class="mentioned-user" href="https://dev.to/broerjuang"&gt;@broerjuang&lt;/a&gt; wrote:&lt;br&gt;&lt;br&gt;
Dung&lt;/p&gt;

&lt;p&gt;On Wed, Jun 03, 2020 at 01.20 AM &lt;a class="mentioned-user" href="https://dev.to/ybbond"&gt;@ybbond&lt;/a&gt; wrote:&lt;br&gt;&lt;br&gt;
ju&lt;/p&gt;

&lt;p&gt;On Wed, Jun 03, 2020 at 01.20 AM &lt;a class="mentioned-user" href="https://dev.to/broerjuang"&gt;@broerjuang&lt;/a&gt; wrote:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/org-roam/org-roam"&gt;https://github.com/org-roam/org-roam&lt;/a&gt;&lt;br&gt;&lt;br&gt;
lol&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He knew about me ranting on Twitter two days earlier for not getting invitation to try &lt;strong&gt;Roam Research&lt;/strong&gt;. He is also a fellow &lt;em&gt;Vimmer&lt;/em&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt;, and I think he knew that I will think twice before using Emacs. I decided to to try it today.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Steps
&lt;/h2&gt;

&lt;p&gt;First, I installed Emacs. If you are on macOS, it is &lt;a href="https://github.com/hlissner/doom-emacs/blob/develop/docs/getting_started.org#with-homebrew"&gt;highly recommended&lt;/a&gt; to use emacs-plus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew tap d12frosted/emacs-plus

brew install emacs-plus --with-modern-icon-cg433n

ln -s /usr/local/opt/emacs-plus/Emacs.app /Applications/Emacs.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then install Doom Emacs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/hlissner/doom-emacs ~/.emacs.d

~/.emacs.d/bin/doom install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I know from experience that I should try the vanilla version of any software before modding it. For Emacs, I have known its basic navigation key bindings. I even use the basic &lt;code&gt;C-b&lt;/code&gt;, &lt;code&gt;C-f&lt;/code&gt;, &lt;code&gt;C-n&lt;/code&gt;, &lt;code&gt;C-p&lt;/code&gt;, &lt;code&gt;C-a&lt;/code&gt; and &lt;code&gt;C-e&lt;/code&gt; instead of arrow keys to navigate most text editor that don’t support Vim key bindings. Even you can use Emacs bindings for the form in the internet with Web Browser. Doom Emacs use Evil mode key bindings by default, that means Vim-like key binding is usable from the start.&lt;/p&gt;

&lt;p&gt;I make myself comfortable with Doom Emacs for an hour, then installing Org-roam. With Doom Emacs, Org-mode is installed by default and I can add &lt;code&gt;+roam&lt;/code&gt; flag in &lt;code&gt;init.el&lt;/code&gt; to install Org-roam. You can also follow the main installation method from &lt;a href="https://github.com/org-roam/org-roam#installation"&gt;the docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mGzLdwf7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-06-org-mode-with-org-roam/1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mGzLdwf7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-06-org-mode-with-org-roam/1.png" alt="Add +roam flag for org" title="Add +roam flag for org" width="880" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also installed the recommended packages &lt;code&gt;deft&lt;/code&gt; and &lt;code&gt;org-journal&lt;/code&gt;. The former is a tool to open any file by searching its title or content text. The later is a complementary framework to make a good timeline based journaling &lt;em&gt;à la&lt;/em&gt; Roam Research. With the tool ready, I try making a daily journal with the shortcut &lt;code&gt;C-c&lt;/code&gt; &lt;code&gt;n&lt;/code&gt; &lt;code&gt;j&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--peuWQBMJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-06-org-mode-with-org-roam/3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--peuWQBMJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-06-org-mode-with-org-roam/3.png" alt="Example of daily journal with Org-roam" title="Example of daily journal with Org-roam" width="880" height="766"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Matter of Personal Preferences
&lt;/h2&gt;

&lt;p&gt;Evil mode is actually an acronym for &lt;em&gt;Extensible Vi Layer&lt;/em&gt;. Its key bindings really makes me feel at home, but it still has some quirks. My first problem is with soft wrapped text. I mapped &lt;code&gt;gj&lt;/code&gt; and &lt;code&gt;gk&lt;/code&gt; to replace default &lt;code&gt;j&lt;/code&gt; and &lt;code&gt;k&lt;/code&gt; in Vim, it enables &lt;strong&gt;per visual line&lt;/strong&gt; movement on wrapped text, instead of &lt;strong&gt;per line number&lt;/strong&gt;. &lt;code&gt;evil-mode&lt;/code&gt; provides &lt;code&gt;evil-respect-visual-line-mode&lt;/code&gt; option, but it doesn’t work in the case of delete whole line with &lt;code&gt;dd&lt;/code&gt; command. With that option enabled, &lt;code&gt;dd&lt;/code&gt; will only delete the current visual line. I installed &lt;a href="https://github.com/YourFin/evil-better-visual-line"&gt;evil-better-visual-line&lt;/a&gt; that try to solve this issue. There is still some quirks, but better than before.&lt;/p&gt;

&lt;p&gt;My second problem is clipboard management. I expect a Vim-like environment to not mess with my system clipboard each time I delete, yank or change any text with Vim key bindings. I want the editor to use its internal register for Vim commands, and only use clipboard if I explicitly use &lt;code&gt;⌘+c&lt;/code&gt; and &lt;code&gt;⌘+v&lt;/code&gt;. For this issue, I asked on Doom Emacs’ discord channel, and got the solution from this &lt;a href="https://github.com/midchildan/dotfiles/blob/master/home/.config/doom/config.org#cutting-and-pasting"&gt;this guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A newbie Emacs user, I configure with Org-roam’s maintainer’s &lt;a href="https://github.com/jethrokuan/dots"&gt;dotfiles&lt;/a&gt; as reference. You can see my Doom Emacs configuration files on my &lt;a href="https://git.ybbond.dev/dotfiles/files.html"&gt;dotfiles repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving from FSNotes
&lt;/h2&gt;

&lt;p&gt;I use FSNotes to keep my notes. It is a great program inspired by nvALT and created by a single person (check out the &lt;a href="https://www.patreon.com/hlushchenko"&gt;Patreon page&lt;/a&gt;!). It features wiki linking and I’ve tried using Zettelkasten paradigm, even though not too successful. I have 300+ good Markdown files in it, guides, personal notes, quotes from internet, many topics.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JkCm88bb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-06-org-mode-with-org-roam/2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JkCm88bb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-06-org-mode-with-org-roam/2.png" alt="Example of product note on Org-roam" title="Example of product note on Org-roam" width="880" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eOG_t90z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-06-org-mode-with-org-roam/4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eOG_t90z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-06-org-mode-with-org-roam/4.png" alt="Comparing FSNotes with Org-roam" title="Comparing FSNotes with Org-roam" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My next move after creating journal with Org-roam is… migrating the Markdown files to Org files. I migrated 2 files at first, then compared between my notes on Org-roam with the one in FSNotes. I actually liked the interface of FSNotes, but the automatic back links, and the Vim bindings… I went “what the hell” and migrated all of those 300+ Markdown files. It took around 2 hours of my afternoon, but I enjoy it.&lt;/p&gt;

&lt;p&gt;There is still issue though.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Org as in Mode and File Extension
&lt;/h2&gt;

&lt;p&gt;I like to do things on my Macbook Pro. I even bring the laptop everywhere. I use it like anyone else use smartphone, as communication tool, scrolling social medias&lt;sup id="fnref2"&gt;2&lt;/sup&gt;, read articles and news. But I’d still like to be able to see my notes on my iPhone or iPad, and this weird *.org file format hindered me on doing so.&lt;/p&gt;

&lt;p&gt;FSNotes has iOS app that synchronizes instantly. Looking on the App Store, Reddit and DuckDuckGo, I only find this app called &lt;a href="https://beorgapp.com/"&gt;Beorg&lt;/a&gt;. By itself, Beorg is a good TODO and Agenda manager based on Org-mode. It won’t accept *.org file outside its iCloud directory, so no go. Other alternative is to install terminal emulator app and Emacs within, but it won’t be a pleasant touch experience.&lt;/p&gt;

&lt;p&gt;Org-mode has a weird syntax to display verbatim text and it doesn’t have syntax for displaying inline code or block code. As a programmer that often store note related to coding, I think this is a minus.&lt;/p&gt;

&lt;p&gt;Thinking long and far, I decided to keep using Org-roam, while waiting to find a good iOS app that allow me to read my notes. Maybe it’s time to make my own iOS application that does just that? Interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Let’s &lt;a href="https://www.youtube.com/watch?v=JWD1Fpdd4Pc"&gt;stop worrying and start loving&lt;/a&gt; &lt;del&gt;Emacs&lt;/del&gt; &lt;strong&gt;Org-mode&lt;/strong&gt;! It is overall a good framework to store notes, add Org-roam and it became many times better! Don’t worry, (Neo)Vim is still my daily driver.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Not sure if this is even a word, but you know… A &lt;strong&gt;Vim&lt;/strong&gt; user? ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Even though I try to use social media as minimum as possible. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>emacs</category>
      <category>pkm</category>
      <category>zettelkasten</category>
      <category>orgmode</category>
    </item>
    <item>
      <title>Remove Specific Files from Old Git Commit</title>
      <dc:creator>Yohanes Bandung Bondowoso</dc:creator>
      <pubDate>Sat, 20 Jun 2020 10:44:20 +0000</pubDate>
      <link>https://dev.to/ybbond/remove-specific-files-from-old-git-commit-h8l</link>
      <guid>https://dev.to/ybbond/remove-specific-files-from-old-git-commit-h8l</guid>
      <description>&lt;p&gt;tldr; Skip to solution »&lt;/p&gt;

&lt;p&gt;Last week, I encountered a merge conflict while trying to sync my git working branch to latest remote &lt;code&gt;master&lt;/code&gt;. Merge conflicts is not a serious problem to me, I am used to resolving conflicts. But the conflict that I encountered is from an auto generated typings file from &lt;code&gt;Apollo Codegen&lt;/code&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Apollo Codegen&lt;/code&gt; file is &lt;code&gt;Flow&lt;/code&gt; or &lt;code&gt;TypeScript&lt;/code&gt; typings for the schema and queries in project that uses GraphQL. The generated file often has &amp;gt;10k lines of code. Trying to resolve conflict in that file will make the text editor unresponsive (even with Vim!).&lt;/p&gt;

&lt;p&gt;So I aborted the sync master (&lt;code&gt;git rebase --abort&lt;/code&gt;), then attempt to remove the changes for the auto-generated codegen file using lazygit. Afterwards, I do sync remote &lt;code&gt;master&lt;/code&gt; branch and the conflict don't happen. Last thing I do is regenerate the codegen file before posting a Pull Request.&lt;/p&gt;

&lt;h2&gt;
  
  
  How-Tos
&lt;/h2&gt;

&lt;p&gt;Back to the post's main topic, to remove specific file using plain old shell command.&lt;/p&gt;

&lt;p&gt;First, checkout to &lt;strong&gt;temporary branch&lt;/strong&gt; with the afore mentioned commit as &lt;code&gt;HEAD&lt;/code&gt; using the commit's hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout &amp;lt;commit-hash&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then do a &lt;strong&gt;soft reset&lt;/strong&gt; to uncommit with all files in staged status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reset &lt;span class="nt"&gt;--soft&lt;/span&gt; HEAD^
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make the desired file(s) unstaged using &lt;strong&gt;reset&lt;/strong&gt; command, and then commit with &lt;code&gt;-c ORIG_HEAD&lt;/code&gt; flag to use the previous commit message. The &lt;code&gt;--no-edit&lt;/code&gt; flag is optional.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reset &amp;lt;path/to/file&amp;gt;

git commit &lt;span class="nt"&gt;-c&lt;/span&gt; ORIG_HEAD &lt;span class="nt"&gt;--no-edit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Discard the changes of the file you want to remove from unstaged area:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Last, &lt;code&gt;rebase&lt;/code&gt; this &lt;strong&gt;temporary branch&lt;/strong&gt; to your branch, from the commit of &lt;code&gt;&amp;lt;commit-hash&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git rebase &lt;span class="nt"&gt;--onto&lt;/span&gt; HEAD &amp;lt;commit-hash&amp;gt; &amp;lt;destination-branch-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do this for already merged Pull Request, you need to &lt;code&gt;push &amp;lt;remote&amp;gt; &amp;lt;branch&amp;gt; --force&lt;/code&gt;. Mind you, doing this will be inconvenient for other people working on same project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lazygit Way
&lt;/h2&gt;

&lt;p&gt;As I mentioned above, I use &lt;a href="https://github.com/jesseduffield/lazygit"&gt;lazygit&lt;/a&gt;. The process is more straight forward. For the following example, I accidentally committed build file, far before I &lt;code&gt;.gitignore&lt;/code&gt;d &lt;code&gt;build/&lt;/code&gt; folder. What I do is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o1pXUCD8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-05-remove-specific-files-from-old-git-commit/1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o1pXUCD8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-05-remove-specific-files-from-old-git-commit/1.png" alt="Navigate to desired commit"&gt;&lt;/a&gt;&lt;/p&gt;
Navigate to desired commit



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--otgML8Cm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-05-remove-specific-files-from-old-git-commit/2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--otgML8Cm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-05-remove-specific-files-from-old-git-commit/2.png" alt="Press return key, and navigate to file to be removed"&gt;&lt;/a&gt;&lt;/p&gt;
Press return key, and navigate to file to be removed



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SP_DTUM4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-05-remove-specific-files-from-old-git-commit/3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SP_DTUM4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-05-remove-specific-files-from-old-git-commit/3.png" alt="Press  raw `x` endraw  will prompt command list. Turns out d will discard the file"&gt;&lt;/a&gt;&lt;/p&gt;
Press &lt;code&gt;x&lt;/code&gt; will prompt command list. Turns out d will discard the file



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uuDEcTwB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-05-remove-specific-files-from-old-git-commit/4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uuDEcTwB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ybbond.dev/posts/2020-05-remove-specific-files-from-old-git-commit/4.png" alt="After pressing  raw `d` endraw , the  raw `Index.js` endraw  file is now removed!"&gt;&lt;/a&gt;&lt;/p&gt;
After pressing &lt;code&gt;d&lt;/code&gt;, the &lt;code&gt;Index.js&lt;/code&gt; file is now removed!



&lt;p&gt;Do this way if you want to install lazygit before doing your intention.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lazygit&lt;/code&gt; way is easier. I also use &lt;a href="https://github.com/jonas/tig"&gt;tig&lt;/a&gt; as a TUI for &lt;code&gt;git&lt;/code&gt;, but I don't know the command to do the steps wit &lt;code&gt;tig&lt;/code&gt;.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Here is &lt;code&gt;Apollo Codegen&lt;/code&gt; &lt;a href="https://github.com/apollographql/apollo-tooling#apollo-clientcodegen-output"&gt;Github&lt;/a&gt; page. As a frontend engineer, I consider &lt;code&gt;codegen&lt;/code&gt; as breakthrough because I can easily type the endpoint's return value. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>git</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>About GoatCounter, The Web Analytics I Use</title>
      <dc:creator>Yohanes Bandung Bondowoso</dc:creator>
      <pubDate>Fri, 22 May 2020 17:47:36 +0000</pubDate>
      <link>https://dev.to/ybbond/about-the-web-analytics-i-use-43dc</link>
      <guid>https://dev.to/ybbond/about-the-web-analytics-i-use-43dc</guid>
      <description>&lt;p&gt;I like to develop website. I also like to write &lt;a href="https://ybbond.dev/tags/blog/"&gt;blogs&lt;/a&gt;, &lt;a href="https://ybbond.dev/tags/poem/"&gt;poems&lt;/a&gt;, post mortem, thoughts, tutorial, anything. I enjoy doing those things that even if I cannot publish (some of) them, I've got more experience and I feel good.&lt;/p&gt;

&lt;p&gt;But.&lt;/p&gt;

&lt;p&gt;Let's be real here. I have itches. Curiosity. Social needs? Maybe, but I try avoid social media, or &lt;a href="https://indieweb.org/silo"&gt;silo&lt;/a&gt; — as the IndieWeb folks call it. I try to serve this site keeping my ethic&lt;sup id="fnref1"&gt;1&lt;/sup&gt; and readers' privacy. I also want to know whether anyone read my posts, and I want my works to be assessed by fellow readers. The assessment can be as simple as likes and thumb-ups (Luke Smith hilariously calls them &lt;em&gt;upcummies&lt;/em&gt;&lt;sup id="fnref2"&gt;2&lt;/sup&gt;). The assessment can also be comments or text feedbacks, this is what I prefer the most.&lt;/p&gt;

&lt;p&gt;With the stuff I just told you in mind, I decided that I will implement analytic&lt;sup id="fnref3"&gt;3&lt;/sup&gt; and response system to this site. So I researched for the tools. The main requirements are lightweight and respects users' privacy.&lt;/p&gt;

&lt;p&gt;For the response system, I found &lt;a href="https://webmention.io/"&gt;WebMention&lt;/a&gt;, which leads me to know more about &lt;a href="https://indieweb.org"&gt;IndieWeb&lt;/a&gt;. The current candidate for the direct commenting system is &lt;a href="https://posativ.org/isso/"&gt;Isso&lt;/a&gt;. The first I implemented is web analytics. Already suggested in this post's title, I use GoatCounter.&lt;/p&gt;

&lt;h2&gt;
  
  
  About GoatCounter
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.goatcounter.com/"&gt;GoatCounter&lt;/a&gt; is web analytics by Martin Tournoij or &lt;a href="https://www.arp242.net/goatcounter.html"&gt;arp242&lt;/a&gt;. It is lightweight, it is easy to implement, and it &lt;strong&gt;respects users' privacy&lt;/strong&gt;. See its privacy policy &lt;a href="https://www.goatcounter.com/privacy"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One embarassing thing is that I follow Martin's RSS Feed for so long, but I didn't read about his latest project. Before GoatCounter, I tried &lt;a href="https://statcounter.com"&gt;StatCounter&lt;/a&gt; and opted-out shortly because I think the settings page is clumsy. I searched for another good alternatives that results none. Just last monday I see Martin published a new post about GoatCounter update. I decided to check it and that day, I tried GoatCounter.&lt;/p&gt;

&lt;p&gt;You can see my instance of GoatCounter on &lt;a href="https://stats.ybbond.dev/"&gt;stats.ybbond.dev&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;I made it open for everyone and set obvious statement down in the &lt;a href="https://ybbond.dev"&gt;footer of my website&lt;/a&gt;, because I think however small the data I take from user visits, the users should be able to know about it.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;A good post from iA, &lt;em&gt;"Ethics" and Ethics&lt;/em&gt; by &lt;strong&gt;Oliver Reichenstein&lt;/strong&gt;. &lt;a href="https://ia.net/topics/ethics-and-ethics"&gt;Read it here&lt;/a&gt; on their site. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Reference is from video &lt;em&gt;Social Media: Anything for Upcummies! ⬆🍆💦💦&lt;/em&gt; by &lt;strong&gt;Luke Smith&lt;/strong&gt;. You can &lt;a href="https://youtu.be/YjbyDU0WzYI?t=48"&gt;watch it here&lt;/a&gt; on YouTube. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;I even stated in my &lt;a href="https://ybbond.dev/posts/2020-04-migrating-my-blog-to-hugo/#current-features"&gt;post&lt;/a&gt; about migrating to Hugo that I will implement &lt;em&gt;tracker&lt;/em&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>analytics</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Why I Positioned Sidebar to the Right</title>
      <dc:creator>Yohanes Bandung Bondowoso</dc:creator>
      <pubDate>Mon, 27 Apr 2020 04:57:12 +0000</pubDate>
      <link>https://dev.to/ybbond/why-i-positioned-sidebar-to-the-right-1150</link>
      <guid>https://dev.to/ybbond/why-i-positioned-sidebar-to-the-right-1150</guid>
      <description>&lt;p&gt;A few minutes ago, I opened twitter and scrolled the timeline.&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--xlkdd6SE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1196879414585200640/gwyQDLMQ_normal.jpg" alt="Simon Sturmer profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Simon Sturmer
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @sstur_
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      &lt;a href="https://twitter.com/rstacruz"&gt;@rstacruz&lt;/a&gt; This is 🔥&lt;br&gt;(I love that you have the tree on the right side. &lt;a href="https://twitter.com/ybbond_"&gt;@ybbond_&lt;/a&gt; told me to do this a long time ago 🤣)
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      19:28 PM - 26 Apr 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1254492579275526145" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1254492579275526145" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1254492579275526145" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;I laughed when I read that tweet. It brings back so much memories of my previous workplace.&lt;/p&gt;

&lt;p&gt;Positioning the sidebar to the right reduces distraction when I toggle it. If the sidebar is on the left, the text shifts around whenever the sidebar is toggled on or off. Readjusting my eyes for the moved text creates disturbance to my logical thinking. “Why are you not just leave the sidebar open?", you might be asking. I want to keep the screen real estate wide.&lt;/p&gt;

&lt;p&gt;Back then, I often toggle the sidebar because two reasons. First, the project I was working on uses &lt;strong&gt;Redux&lt;/strong&gt; - &lt;strong&gt;Saga&lt;/strong&gt;. Second, I used to use &lt;strong&gt;VSCode&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Redux-Saga is a great states and IO management for React, I was blown away when I was first using it. Sadly, cannot grok the Redux paradigm easily. I often need to see the file structure when I write new files or refactoring. My current project uses Apollo GraphQL and Domain Driven Development paradigm. I am comfortable using the current setup.&lt;/p&gt;

&lt;p&gt;In VSCode, manipulating file must be done with mouse. Using the built in &lt;strong&gt;Git&lt;/strong&gt; also done in the sidebar, with mouse. Nowadays I use &lt;strong&gt;NeoVim&lt;/strong&gt;. File management can be done easily with text command. If a more complex file management must be done, which is rare, I use &lt;strong&gt;NerdTree&lt;/strong&gt;. Versioning with Git is done with the help of &lt;strong&gt;vim-fugitive&lt;/strong&gt; which I found less disturbing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;As a closing&lt;/strong&gt;: if you often toggle the sidebar, try positioning it to the right. That is, if your editor supports that configuration. It might help reduce the distraction when you need it the most.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>editor</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
