Index on Michał Góral https://goral.net.pl/ Recent content in Index on Michał Góral Hugo -- gohugo.io en Fri, 29 Mar 2019 12:00:00 +0000 On Independent Press https://goral.net.pl/post/on-independent-press/ Thu, 16 Jul 2020 00:00:00 +0000 https://goral.net.pl/post/on-independent-press/ <p>It is interesting seeing different opinions on flaws of today&rsquo;s journalism and its lack of objectivity or even ability to simply &ldquo;connect the dots&rdquo;. Professor Noam Chomsky in <a href="https://www.nationalobserver.com/2019/02/12/features/noam-chomsky-couple-generations-organized-human-society-may-not-survive-has-be">interview</a> for <em>National Observer</em> recognizes that &ldquo;the advertising-profit model for media has just undermined journalism&rdquo;. On the other hand <em>The Economist</em> <a href="https://www.economist.com/books-and-arts/2020/07/15/how-objectivity-in-journalism-became-a-matter-of-opinion">reports</a> that &ldquo;today, as ad revenues leak away to search engines and social networks, newspapers have come to rely more on paying readers.&rdquo;</p> <p>These two contrary points of view don&rsquo;t neccessarily exclude each other. By massaging their readers&rsquo; egos, magazines and newspapers maintain their profiles and keep advertisers. It isn&rsquo;t surprising that ads published in <em>The Financial Times</em> are different than in Red Tops, British tabloids with red mastheads. This naturally leans publishers towards certain viewpoints, narrows scope of publications and eventually leads to reporters&rsquo; self-censorship.</p> <p>It&rsquo;s no secret that capital quickly leaves print press in favor of tracking target audience in digital world, but this doesn&rsquo;t improve the situation of journals at all. There&rsquo;s no true freedom if one has to rely on someone else&rsquo;s money as agendas of people who own it always stay afloat. Professor Chomsky reminds that in its early days United States &ldquo;subsidized things like free postal rates, which were devices to try to create an independent press&rdquo;, which raises a question why don&rsquo;t we have similar programmes today. Bad news is that when state starts throwing public money at press or any other media, it creates yet another gear in its propaganda machine and it ultimately works against society. But subsidizing delivery networks or libraries in even small towns where one could go and read for free what&rsquo;s currently available is another pair of boots.</p> <p>It seems that unless publishers stockpile money, their true independence is impossible. But what if that&rsquo;s good? Maybe some self-censorship works for the higher quality of journalism? After all, if journalists had no moral breaks and could say whatever they want, we would probably end up with another Twitter clone.</p> <p>But perhaps we&rsquo;re seeking Plato&rsquo;s shadows on the wall and journalism was never free nor objective. It is possible that it doesn&rsquo;t have to be. The problem might lie deeper, in miserable state of today&rsquo;s public debate. Even partisan journalism adds value if it can be compared with points of opposing party and true objectivity can be only achieved when readers are able to easily analyze different opinions. We don&rsquo;t have platforms to do that. Classical media offer only a single perspective on most topics, without even mentioning any others. The internet technically allows that but news aggregators and social media, two most popular platforms, are in fact filters which censor any dissent. Search engines are no better: destroyed by search engine optimization techniques, you&rsquo;re more likely to find a t-shirt opposing (or approving) current affairs than read different takes on them. This might be small exaggeration, but not far from the truth. For most consumers it just tightens their information bubble.</p> <p>Language of today journalism also poses a problem. It&rsquo;s hard to have any discussion at all when we stop evaluating things on their merits and start categorizing them, typically as left- and right-wing. This polarizes society, journals and advertisers. It&rsquo;s classical example of <em>divide and conquer</em> tactics in which newspapers got trapped with no easy way out, at least without losing some of their readers and advertisers. Which they won&rsquo;t do because in times of print press recession it would be as good as suicide.</p> <p>We need high quality journalism more than ever and we need it now. One which can be trusted and depended on. This is the key point with which Professor Chomsky and <em>The Economist</em> agree. How to achieve it and can we afford it? These are the open questions.</p> Monochrome Vim Color Scheme https://goral.net.pl/post/monochrome-vim-colorscheme/ Tue, 14 Jul 2020 00:00:00 +0000 https://goral.net.pl/post/monochrome-vim-colorscheme/ <p>Rob Pike once said that syntax highlighting is juvenile. This is very bold statement and I wouldn&rsquo;t go that far. In my opinion some typographical elements are necessary to break boredom of walls of texts, but it doesn&rsquo;t have to be color, which has a natural tendency to quickly introduce unbearable noise. To prove that I have created <a href="https://git.goral.net.pl/mgoral/vim-bw">bw.vim</a>: a minimalistic monochrome color scheme.</p> <h2 id="monochrome-text-is-good-for-your-eyes-and-soul">Monochrome Text Is Good for Your Eyes and Soul</h2> <p>I believe that when it comes to text, less is more. Take classical newspapers which, although packed with content, are beautifully readable thanks to typesetting. I think that correct use of fonts is the key for emphasizing important parts without bringing unnecessary distractions.</p> <p>Consider the following screenshot which compares <a href="https://github.com/gruvbox-community/gruvbox">Gruvbox</a>, one of the most popular color schemes for Vim and bw.vim:</p> <figure class="center"> <a href="https://goral.net.pl/post/monochrome-vim-colorscheme/gruvbox-vs-bw.png"> <img src="https://goral.net.pl/post/monochrome-vim-colorscheme/gruvbox-vs-bw.png" alt="Side-by-side screenshot of same Python code highlighted by Gruvbox and bw.vim" /> </a> <figcaption><p>Gruvbox vs bw.vim</p></figcaption> </figure> <p>See what I mean when I said about noise? In bw.vim instead of highlighting every second word I try to emphasize only the most important bits which are the ones that create the structure of file: conditional blocks, function definitions, titles and headers. We can&rsquo;t play with different font faces or sizes because we are constrained with capabilities of underlying terminal, but we can replace colors with very basic typesetting: bolds, italics and underlines. It&rsquo;s still a lot and I find it far better for meaningful deep work.</p> <p>I&rsquo;m pragmatic though. Colors have their use and can be a powerful tool when used sparingly. For example reds and greens, after decades of their presence, are now intrinsically ingrained in diffs. My <a href="https://goral.net.pl/post/notes-2/">notes</a> have blue links because inter-connections between them are single most important feature of Zettelkasten-style notes, worth breaking the rules. <a href="https://github.com/justinmk/vim-dirvish">Dirvish</a> (a file browser) has colorful directories because it helps navigation; utilitarism wins.</p> <h2 id="light-theme">Light Theme</h2> <p>Apparently light themes are more readable. People read dark fonts on light backrounds more accurately<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and specifically people with astigmatism (approx. 30-50% of population) find it harder to read otherwise<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. After all, books have mastered black fonts on white background over centuries and we&rsquo;re all used to them, even if they don&rsquo;t emit light, in contrast to computer screens.</p> <p>Recently, after many years of ignoring light themes, I have slowly started acknowledging their existence again. Today I find them superior especially in very bright rooms, at midday, in the middle of summer.</p> <p>This is why bw.vim ships with both light and dark variants. It automatically detects which one should be used by examining <a href="https://vimhelp.org/options.txt.html#%27background%27"><code>:help background</code></a>. Vim by itself reloads color scheme when background changes so I created a simple function to toggle it during different times of a day<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>:</p> <div class="highlight"><pre class="chroma"><code class="language-vim" data-lang="vim"><span class="k">function</span><span class="p">!</span> <span class="nx">togglebg</span>#<span class="nx">togglebg</span><span class="p">()</span><span class="err"> </span><span class="err"></span> <span class="k">let</span> &amp;<span class="nx">background</span> <span class="p">=</span> <span class="p">(</span>&amp;<span class="nx">background</span> <span class="p">==</span> <span class="s2">&#34;dark&#34;</span> ? <span class="s2">&#34;light&#34;</span> : <span class="s2">&#34;dark&#34;</span><span class="p">)</span><span class="err"> </span><span class="err"></span><span class="k">endfunction</span><span class="err"> </span><span class="err"> </span><span class="err"></span><span class="nx">command</span><span class="p">!</span> <span class="nx">ToggleBg</span> <span class="nx">call</span> <span class="nx">togglebg</span>#<span class="nx">togglebg</span><span class="p">(&lt;</span><span class="nx">f</span><span class="p">-</span><span class="nx">args</span><span class="p">&gt;)</span><span class="err"> </span><span class="err"></span><span class="nx">nnoremap</span> <span class="p">&lt;</span><span class="nx">silent</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nx">leader</span><span class="p">&gt;&lt;</span><span class="nx">leader</span><span class="p">&gt;</span><span class="nx">t</span> :<span class="nx">ToggleBg</span><span class="p">&lt;</span><span class="nx">cr</span><span class="p">&gt;</span><span class="err"> </span></code></pre></div> <figure class="center"> <a href="https://goral.net.pl/post/monochrome-vim-colorscheme/bw-light.png"> <img src="https://goral.net.pl/post/monochrome-vim-colorscheme/bw-light.png" alt="Light variant of bw.vim" /> </a> <figcaption><p>Light variant of bw.vim</p></figcaption> </figure> <h2 id="extending-bwvim">Extending bw.vim</h2> <p>Changing how bw.vim highlights different color groups is really easy, thanks to <code>hi</code> function inherited from Fogbell. Have a look at an example:</p> <div class="highlight"><pre class="chroma"><code class="language-vim" data-lang="vim"><span class="nx">call</span> <span class="p">&lt;</span><span class="nx">sid</span><span class="p">&gt;</span><span class="nx">hi</span><span class="p">(</span><span class="s1">&#39;asciidocURL&#39;</span><span class="p">,</span> <span class="nx">s</span>:<span class="nx">voidCream</span><span class="p">,</span> <span class="nx">s</span>:<span class="nx">none</span><span class="p">,</span> <span class="s1">&#39;underline&#39;</span><span class="p">)</span><span class="err"> </span></code></pre></div><p>With this the hard part remains figuring out names of color groups. I made it easier with the following mapping which reports names of color groups under the cursor:</p> <div class="highlight"><pre class="chroma"><code class="language-vim" data-lang="vim"><span class="nx">map</span> <span class="p">&lt;</span><span class="nx">F3</span><span class="p">&gt;</span> :<span class="nx">echo</span> <span class="s2">&#34;hi&lt;&#34;</span> . <span class="nx">synIDattr</span><span class="p">(</span><span class="nx">synID</span><span class="p">(</span><span class="nx">line</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">),</span><span class="nx">col</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">),</span><span class="m">1</span><span class="p">),</span><span class="s2">&#34;name&#34;</span><span class="p">)</span> . <span class="s1">&#39;&gt; trans&lt;&#39;</span><span class="err"> </span><span class="err"></span>\ . <span class="nx">synIDattr</span><span class="p">(</span><span class="nx">synID</span><span class="p">(</span><span class="nx">line</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">),</span><span class="nx">col</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">),</span><span class="m">0</span><span class="p">),</span><span class="s2">&#34;name&#34;</span><span class="p">)</span> . <span class="s2">&#34;&gt; lo&lt;&#34;</span><span class="err"> </span><span class="err"></span>\ . <span class="nx">synIDattr</span><span class="p">(</span><span class="nx">synIDtrans</span><span class="p">(</span><span class="nx">synID</span><span class="p">(</span><span class="nx">line</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">),</span><span class="nx">col</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">),</span><span class="m">1</span><span class="p">)),</span><span class="s2">&#34;name&#34;</span><span class="p">)</span> . <span class="s2">&#34;&gt;&#34;</span><span class="p">&lt;</span><span class="nx">CR</span><span class="p">&gt;</span><span class="err"> </span></code></pre></div> <figure class="center"> <a href="https://goral.net.pl/post/monochrome-vim-colorscheme/bw-notes.png"> <img src="https://goral.net.pl/post/monochrome-vim-colorscheme/bw-notes.png" alt="Screenshot of one of my notes" /> </a> <figcaption><p>Note with highlighted links</p></figcaption> </figure> <p>Monochrome color schemes aren&rsquo;t particularly novel idea. Before creating my own, I had checked at least half a dozen of other color schemes. My adaptation to no-color environment was practically painless and now I am thinking about switching my terminal, window manager and other programs to <abbr title="black and white">B&amp;W</abbr> . I don&rsquo;t miss anything from color schemes that I used previously and the minimalism is refreshening. Try it yourself for a day or two, maybe you&rsquo;ll like it as well!</p> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p>Bauer, D., &amp; Cavonius, C., R. (1980). Improving the legibility of visual display units through contrast reversal. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>Jason Harrison – Post Doctoral Fellow, Imager Lab Manager – Sensory Perception and Interaction Research Group, University of British Columbia. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:3" role="doc-endnote"> <p>I plan to automate it. <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section> Double fork https://goral.net.pl/post/double-fork/ Wed, 08 Jul 2020 00:00:00 +0000 https://goral.net.pl/post/double-fork/ <p>Roughly 4 years ago we learned about <a href="https://goral.net.pl/post/sforkowalem-proces/">fork()</a> function. Sometimes when we want the process to become a daemon, i.e. to run in background by detaching from a controlling terminal without a possibility to reattach, it&rsquo;s useful to perform a technique known as <em>double fork</em>.</p> <p>The steps are:</p> <ol> <li>the parent forks to create the child and exits;</li> <li>the child becomes a new session leader by calling <code>setsid()</code>;</li> <li>the child calls <code>fork()</code> again and exits.</li> </ol> <p>Creating a new session during step 2 detaches child from parent&rsquo;s TTY, but now as a session leader, the process could acquire TTY (accidentally or intentionally), which is unwanted for daemons. To prevent that, second fork (a grandchild) is created, which is not a session leader and thus cannot acquire a terminal.</p> <p>So strictly speaking I forked twice and not double forked (maybe I will one day), but also strictly speaking I am not a UNIX environment.</p> <figure class="center"> <a href="https://goral.net.pl/post/double-fork/usg.jpg"> <img src="https://goral.net.pl/post/double-fork/usg_hucc68ce18078b152e107e8ebede85320a_221145_720x0_resize_q90_box.jpg" alt="Ultrasound photo" /> </a> <figcaption><p>We are the fork. Lower your shields and surrender your ships. We will add your biological and technological distinctiveness to our own. Your culture will adapt to service us. Resistance is futile.</p></figcaption> </figure> First Drawing https://goral.net.pl/post/first-drawing/ Fri, 19 Jun 2020 00:00:00 +0000 https://goral.net.pl/post/first-drawing/ <p>Apparently Przemek figured out how to draw shapes and, what a surprise, decided to draw his family. Also, it seems I am no different to other parents and will bore to death every single poor soul in 150 meters radius with things our children made. So here it is.</p> <figure class="center"> <a href="https://goral.net.pl/post/first-drawing/drawing.jpg"> <img src="https://goral.net.pl/post/first-drawing/drawing.jpg" alt="Drawing made by child" /> </a> <figcaption><p> From the left: mommy, for some reason holding a purple ball, then daddy without one eye because it fell off in most mysterious circumstances and on the right the artist's self-portrait.</p></figcaption> </figure> Accessibility testing by non-disabled developers https://goral.net.pl/post/accessibility-testing-by-non-disabled-developers/ Thu, 11 Jun 2020 00:00:00 +0000 https://goral.net.pl/post/accessibility-testing-by-non-disabled-developers/ <p>Whenever I create a website, at some point there always arises an issue of its accessibility for people with disabilities. Mostly we&rsquo;re talking about users of screen readers. Problem is that I don&rsquo;t use screen reader myself for day-to-day activities anddesign of these programs remains uncharted territory for me so no matter how hard I try, I can be never sure that I achieve intended effect.</p> <p>Similar impulse struck me few days ago when I decided to check how accessible my personal website is and to fix any possible problems. I focused on screen readers because they&rsquo;re arguably the most widespread Assistive Technology used not only by visually impaired, but also by people with other types of disabilities, according to <a href="https://webaim.org/projects/screenreadersurvey8/#disabilitytypes">WebAIM survey</a>.</p> <p>I have already increased contrast of pages few years ago and default font is quite big with lines not too long to ease eye strain. This more-less covers people with low vision who occassionally use screen readers or software magnifying glass. Next steps mostly touch skeleton of site: its HTML code. Two particular documents are great resources for working with accessible HTML:</p> <ol> <li><em><a href="https://www.w3.org/TR/aria-in-html/">Using <abbr title="Accessible Rich Internet Applications">ARIA</abbr> </a></em>, a very helpful knowledge refresher, covering most of the principles which should be followed by developers who want to make their sites better places;</li> <li><em><a href="https://www.w3.org/TR/html-aria/#allowed-aria-roles-states-and-properties">Allowed ARIA roles, states and properties</a></em>, which provides a list of ARIA roles allowed in HTML which are important for screen readers, especially when creating non-standard widgets (e.g. buttons acting like links).</li> </ol> <p>On top of that there are some static validators for HTML correctness which also have some focus on accessibility features of web pages. They&rsquo;re extremly helpful because even with all that knowledge it&rsquo;s still easy to miss some spots. I tried two of them: <a href="https://wave.webaim.org">WAVE</a> and <a href="https://validator.w3.org/nu/">Nu Html Checker</a> and was very happy with how they worked.</p> <p>But of course the most important test is actually hearing how website sounds. Because I don&rsquo;t use Windows, I can&rsquo;t test against the most popular readers: NVDA and JAWS. On Linux the most successful screen reader is Orca so I decided to try it first.</p> <p>This is when problems started.</p> <h2 id="orca">Orca</h2> <p>It took me 2 days to force Orca to read websites, but maybe it&rsquo;s all on me<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. I didn&rsquo;t realise that Orca only detects new windows which are created after it was started itself, and is, sorry for pun, blind for the others. Unless it isn&rsquo;t, like for nm-applet, which worked correctly from the beginning. Under the hood Orca uses AT-SPI DBus interface to communicate with other programs. Not knowing the details of the interface it is equally possible that Orca didn&rsquo;t notify programs about its presence and that programs didn&rsquo;t emit or subscribe for correct events.</p> <p>As end-user I shouldn&rsquo;t be troubled by any of this and I hate to say that this bad initial experience heavily impacted my whole feelings about using screen readers. I&rsquo;m sorry for users who aren&rsquo;t tech-savy and have to deal with problems like that. Frankly, I&rsquo;m not even sure if I&rsquo;d be able to install Orca at all if I were blind. Debian promises in its <a href="https://www.debian.org/releases/buster/i386/ch05s02">installer&rsquo;s manual</a> to automatically install support for software speech synthesis if it was activated during installation but I assume it means espeak or other <abbr title="Text to speech">TTS</abbr> package and not screen reader. I hope that appropriate screen reader is eventually installed and that it is enabled automatically when graphical environment like GNOME or KDE is selected.</p> <p>Let&rsquo;s move on. Apart from its preferences window Orca doesn&rsquo;t provide any visual feedback. I&rsquo;m sure it&rsquo;s partially because desktop computers graphical libraries are quite fragmented so I don&rsquo;t blame Orca here. I simply think that additional indicators (e.g. a frame around currently read paragraph) would be extremely helpful for people who are not completly blind.</p> <p>When Orca finally started reading web pages, I tried the following things, which should be the most common for visually impaired users:</p> <ul> <li>displaying and jumping between all headings and links;</li> <li>jumping through paragraphs back and forth;</li> <li>listing all links and jumping through them.</li> </ul> <p>Everything was going smoothly when suddenly I lost ability to navigate documents. I think I pressed wrong keys accidentally: keyboard shortcuts for screen readers are quite esoteric so it isn&rsquo;t hard to make mistakes. I couldn&rsquo;t bring back Orca to the state in which I&rsquo;d be able to perform my tests and I had to restart it to continue tests.</p> <h2 id="talkback">TalkBack</h2> <p>After my initial failures with Orca I tried TalkBack, a screen reader for Android produced by Google. It is licensed under a free Apache 2.0 license and is freely available from <a href="https://f-droid.org/en/packages/com.google.android.accessibility.talkback/">F-Droid store</a>, but it&rsquo;s an old and buggy version there. If you don&rsquo;t mind using Google Play Store, then version available there is more stable and free of bugs I found in F-Droid&rsquo;s version.</p> <p>TalkBack offers gesture interface which entirely replaces the ordinary way of interacting with <abbr title="Operating System">OS</abbr> . For example you can&rsquo;t click links just like that. Instead you have to select them and double tap anywhere on the screen to actually click them. Links can be selected e.g. by using gestures which walk all elements of foreground program. Other gestures show status bar and handle other common activities. When element is selected, text associated with it is read by system TTS service. Default Lineage OS TTS engine (Pico TTS) didn&rsquo;t work on my phone, probably because it doesn&rsquo;t support language of my system. Espeak worked but it crashed when I tried to configure it. Android phones with pre-installed GApps should have non-free Google TTS available which arguably provides much better experience. If you&rsquo;re privacy-concious and have a rooted phone, you can download Google TTS directly from Google Play Store, then download necessary offline language packs and then block internet access for the whole application via AFWall.</p> <p>Time for bad things. In my opinion TalkBack is too sensitive by default (but I think it can be configured somehow - there are lots of options which I didn&rsquo;t check). Way too often I scrolled through several elements at once when I intended to scroll only one. If I were blind, I&rsquo;d constantly miss everything!</p> <p>Another thing is that AnySoftKeyboard, otherwise great software keyboard, refused to work with screen reader at all and I had to switch to AOSP keyboard just to type anything. I filed a <a href="https://github.com/AnySoftKeyboard/AnySoftKeyboard/issues/2316">bug</a> on ASK&rsquo;s Github page and hopefully its maintainers will pick it up<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p> <p>Once again my tests were succesful. Although I failed to discover how to quickly scan page by headings, I was able to read it element-by-element with no problems.</p> <h2 id="afterthoughts">Afterthoughts</h2> <p>I stopped my tests after checking these two screen readers. The whole experience was very unpleasant, took way too long and I don&rsquo;t feel like I have tested my simple site thoroughly. I had problems with installing and using assistive applications almost all the time. I&rsquo;m sure that partially it&rsquo;s on me and my lack of experience with this type of technology, but come on! Installing assistive technology should be guided and intuitive from the very first second! Why do I have to read manuals or learn about esoteric techniques of enabling screen readers during OS installation/configuration?</p> <p>For example Android requires connected headphones during initial configuration to enable TalkBack system-wide, otherwise one has to navigate to the bottom of settings menu and enable it there &ndash; without a sight. Maybe it&rsquo;s a clever trick but a message from phone&rsquo;s main speaker saying e.g. &ldquo;Hello, to use a screen reader connect your earphones&rdquo; would be much friendlier in my opinion.</p> <p>Shortcuts used by Orca are in my opinion worth at least a paragraph or two of criticism which I&rsquo;m not going to write because it might be speaking my inability to comprehend problems of visually impaired people. However, from a perspective of non-disabled developer, who wants to test his rather straightforward application, screen readers are extremely hard to use. I think they should provide modes with visual hints for non-disabled people, like popups or tray bar icons where you could simply click and select wanted actions. Without aid like this it becomes very hard and frustrating to test applications against screen readers, especially for independent developers with limited resources. I&rsquo;m not surprised that many simply don&rsquo;t test them at all.</p> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p>Donald Norman would very likely disagree. According to his book, <em>The Design of Everyday Things</em>, incorrect usage of things is almost never a fault of users, but it&rsquo;s a failure of bad design of these things; according to him users too frequently blame themselves when they should blame designers. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>When I was writing this article, it remained unanswered. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section> RSS and Other Site News https://goral.net.pl/post/rss-and-other-site-news/ Sat, 30 May 2020 00:00:00 +0000 https://goral.net.pl/post/rss-and-other-site-news/ <p>I think I fixed RSS feed and now it includes new <a href="https://goral.net.pl/projects">projects</a> section which I added few days ago. There are now 2 main feeds:</p> <ul> <li><a href="https://goral.net.pl/feed">main feed</a> which has all content available;</li> <li><a href="https://goral.net.pl/post/index.xml">blog feed</a> which has only blog posts, so it works like main feed had worked previously.</li> </ul> <p>It&rsquo;s also possible to subscribe to individual tags, projects etc. How to do that is described on a separate <a href="https://goral.net.pl/rss/">RSS info page</a>. RSS for all projects is a bit empty though, because it doesn&rsquo;t list all leaf projects recursively. Maybe I&rsquo;ll fix that sometime later.</p> <p>I also added <a href="https://goral.net.pl/archive/">archive page</a> which simply lists all blog posts grouped by year in which they were published.</p> <p>I like the results and that my website starts feeling personal again. It&rsquo;s nice to tinker a bit here and there for half an hour every evening before sleep finally falls on me. It amazes me how much Hugo, a static page generator which is a backbone for my site, can be bent to my will.</p> Niesulice https://goral.net.pl/projects/photography/niesulice/ Thu, 28 May 2020 00:00:00 +0000 https://goral.net.pl/projects/photography/niesulice/ <p>From time to time we come back to <a href="https://www.openstreetmap.org/#map=16/52.2114/15.4045">Niesulice</a>, a small village over Niesłysz lake. I think it was circa 2005 when we discovered this place with a group of friends. Without a car and with outdated summer bus schedules we almost had to bribe bus driver to drop us as close as he could without breaking his schedule, and we still had over an hour of walk in the middle of nowhere. I love that memory.</p> <h2 id="2015">2015</h2> <figure class="center"> <a href="https://goral.net.pl/projects/photography/niesulice/01.jpg"> <img src="https://goral.net.pl/projects/photography/niesulice/01_hu58a04d21348c4bb78cf77c7d303402a6_396595_720x0_resize_q90_box.jpg" alt="A picture of a tree growing from a trunk in the middle of a lake" /> </a> <figcaption><p>This is probably one of my favourite photos. There's nothing impossible for life which will flourish even in most dire circumstances. Here: a small tree tries to grow from a trunk in the middle of a lake.</p></figcaption> </figure> <figure class="center"> <a href="https://goral.net.pl/projects/photography/niesulice/02.jpg"> <img src="https://goral.net.pl/projects/photography/niesulice/02_hu355ad67212968b850dfad484d21f2230_274519_720x0_resize_q90_box.jpg" alt="A picture of a broken bus stop sign" /> </a> </figure> <figure class="center"> <a href="https://goral.net.pl/projects/photography/niesulice/03.jpg"> <img src="https://goral.net.pl/projects/photography/niesulice/03_hu6ed92f3029addecf52e61e825b401230_131972_720x0_resize_q90_box.jpg" alt="A picture of sunset" /> </a> </figure> <figure class="center"> <a href="https://goral.net.pl/projects/photography/niesulice/04.jpg"> <img src="https://goral.net.pl/projects/photography/niesulice/04_hu8957c4bd2d14d692258b0b28ddb5f489_480168_720x0_resize_q90_box.jpg" alt="A picture of people playing volleyball with 2 cans of beer in the foreground" /> </a> </figure> <figure class="center"> <a href="https://goral.net.pl/projects/photography/niesulice/05.jpg"> <img src="https://goral.net.pl/projects/photography/niesulice/05_hu4f3ad503a79c7690059e90d06e3578d2_511210_720x0_resize_q90_box.jpg" alt="A picture of sky with trees around" /> </a> </figure> <figure class="center"> <a href="https://goral.net.pl/projects/photography/niesulice/06.jpg"> <img src="https://goral.net.pl/projects/photography/niesulice/06_hu1eb12c42d9e7c33aca6807aa5fe82423_335395_720x0_resize_q90_box.jpg" alt="A picture of a woman holding a paper cup of coffee" /> </a> </figure> Exotic Animals in Kaszuby https://goral.net.pl/projects/photography/exotic-kaszuby/ Tue, 26 May 2020 00:00:00 +0000 https://goral.net.pl/projects/photography/exotic-kaszuby/ <p>During our vacations in Kaszuby (part of Poland) in 2019, we went to <em>Park Edukacyjny Zoo Egzotyczne Kaszuby</em><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, which is a kind of mini-zoo, besieged by school and family trips. Photos were taken on July 23, 2019.</p> <figure class="center"> <a href="https://goral.net.pl/projects/photography/exotic-kaszuby/01.jpg"> <img src="https://goral.net.pl/projects/photography/exotic-kaszuby/01_huddeb71fd157123423c40243b67bd0278_147005_720x0_resize_q90_box.jpg" alt="A picture of a spider" /> </a> </figure> <figure class="center"> <a href="https://goral.net.pl/projects/photography/exotic-kaszuby/02.jpg"> <img src="https://goral.net.pl/projects/photography/exotic-kaszuby/02_hu0747de9880296a20883d02d9a8204dda_209148_720x0_resize_q90_box.jpg" alt="A picture of a spider" /> </a> </figure> <figure class="center"> <a href="https://goral.net.pl/projects/photography/exotic-kaszuby/03.jpg"> <img src="https://goral.net.pl/projects/photography/exotic-kaszuby/03_hu1da3babd2d0c1723caab9fb99e28d79e_236934_720x0_resize_q90_box.jpg" alt="A picture of a spider" /> </a> </figure> <figure class="center"> <a href="https://goral.net.pl/projects/photography/exotic-kaszuby/04.jpg"> <img src="https://goral.net.pl/projects/photography/exotic-kaszuby/04_hu28d3a9938e0ea37fef40421fbf6ca2b7_177152_720x0_resize_q90_box.jpg" alt="A picture of lizard" /> </a> </figure> <figure class="center"> <a href="https://goral.net.pl/projects/photography/exotic-kaszuby/05.jpg"> <img src="https://goral.net.pl/projects/photography/exotic-kaszuby/05_hu87310b05eeb5933862ccb8aab68ad3ed_174929_720x0_resize_q90_box.jpg" alt="A picture of cameleon" /> </a> </figure> <figure class="center"> <a href="https://goral.net.pl/projects/photography/exotic-kaszuby/06.jpg"> <img src="https://goral.net.pl/projects/photography/exotic-kaszuby/06_hu621e0ee51a3d605f888fe4998319b566_161786_720x0_resize_q90_box.jpg" alt="A picture of cameleon" /> </a> </figure> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p><a href="https://www.openstreetmap.org/search?query=54.305006%2017.859005#map=19/54.30501/17.85901">OpenStreetMap</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section> Your Pain Is Bigger Than Mine https://goral.net.pl/post/your-pain-is-bigger-than-mine/ Wed, 20 May 2020 00:00:00 +0000 https://goral.net.pl/post/your-pain-is-bigger-than-mine/ <p>Few months ago I almost entirely stopped listening to Polish Radio Trójka. It happened when Wojciech Mann finally quit due to firing Anna Gacek. Or rather due to forcing her to resign. I adored his programs and felt that this radio won&rsquo;t be ever the same without his legendary voice.</p> <p>Unfortunately Mr Mann wasn&rsquo;t the first one to quit. In the last few years we saw many others resigning, mostly because of the political atmosphere in a country and in public media. Thankfully for Trójka, it attracted talents like no other radio station and even though some people left, many others remained. But it has changed.</p> <p>It shocked me and saddened when I heard that last Saturday the most recognised music chart in Poland, Lista Przebojów Programu Trzeciego, was censored because <a href="https://www.youtube.com/watch?v=o9LzNtpjhV0">the winner</a> critcizes leader of the ruling party, Jarosław Kaczyński. After this event Marek Niedźwiedzki (author and host of Lista Przebojów), Piotr Metz (Music Director) and Marcin Kydryński quit. President of Trójka asked Bartosz Gil, who had helped to count the votes, to sign a statement saying that chart was manipulated by Mr Niedźwiedzki. He refused and was immediately suspended. Today Piotr Stelmach quit. Piotr Baron refused to co-host any next Lista Przebojów, but he&rsquo;ll likely quit in June as well, after he&rsquo;s back from vacations. The list grows with every transmitted program when another reporters say their goodbyes. Now it&rsquo;s hard to say who&rsquo;s left.</p> <p>While I don&rsquo;t believe government or any high-level politician had anything to do with it, the situation shows the sad state of public media in Poland. It is owned and managed by people who fear other people sitting above them on the ladder of power, and will do anything to please them &ndash; sometimes autonomously, sometimes not. Ultimately the goal is to please the single person on high top of that ladder.</p> <p>It hurts me to think what Trójka has turned into and I&rsquo;m afraid that it won&rsquo;t recover anytime soon. Regime methods don&rsquo;t fit very well to the station with such history, which was a place of rest and a breath of fresh air for its listeners, even before 1989.</p> <p>I wish well to all the reporters who decided to quit and to those who stay despite all of things happening right now. I hope that one day we will hear you once again on better, free frequencies.</p> My First Bread https://goral.net.pl/post/my-first-bread/ Wed, 15 Apr 2020 00:00:00 +0000 https://goral.net.pl/post/my-first-bread/ <p>I baked a bread. No, food shortages caused by people overbuying and overeating during COVID-19 lockdown didn&rsquo;t force me to start making my own food, but maybe thanks to this pandemy I finally have some time to do so.</p> <p>Frankly it&rsquo;s all on my wife. Although I always wanted to try it, I was too lazy to learn about all the details. So when she brought this topic to the table I thought that maybe it&rsquo;s now or never: she can learn everything and then she can teach me. Of course it was harder than that as I refused being taught because I like discovering things myself. A paradox. It&rsquo;s not easy to bear with me.</p> <p>So we ordered yeast and two types of flour (rye and wheat). In times of pandemy it was harder than it should be, but not impossible for a determined couple. Recipe on the other hand was easy to find - there are thousands of them on the internet. Now, after 2 weeks since our first bread, we have roughly 10 of them on our account. It means that we&rsquo;re still amateurs, but very fulfilled ones. Plus every single of our breads tasted far better than the ones we bought before.</p> <p>Baking is easier than I thought, especially amateur version which uses yeast instead of sourdough (but we&rsquo;ll get there, don&rsquo;t worry). It takes about 30 minutes of making a dough, then few hours until it proves, but it depends on the kind of bread, then 30-50 minutes of baking. We even started baking 2 breads at a time because otherwise the one huge loaf was too hard to cut even with our biggest knife and it was quickly getting old and stale.</p> <p>After few initial trials we ordered 25 kg of flour. It was unexpectedly delivered in a huge white sack - looking exactly like you probably imagine huge white sack of flour. Let me tell you: 25 kg is no joke. It is big, it is heavy and it spills flour every time we try to move it. Every time I look at it I think about Settlers (the game) - these small, poor workers carrying white little bags around the village, without a single word, not even a curse. Yeah, bullshit.</p> <p>So, bread is now officially checked-off from my list. Buns are next.</p> <p> <figure class="center"> <a href="https://goral.net.pl/post/my-first-bread/bread1.jpg"> <img src="https://goral.net.pl/post/my-first-bread/bread1.jpg" alt="Dough" /> </a> <figcaption><p>Dough.</p></figcaption> </figure> <figure class="center"> <a href="https://goral.net.pl/post/my-first-bread/bread2.jpg"> <img src="https://goral.net.pl/post/my-first-bread/bread2.jpg" alt="Dough inside auminum foil" /> </a> <figcaption><p>We use aluminum foil to prevent dough from sticking to the glass bowl. And to save it from the influence of 5G.</p></figcaption> </figure> <figure class="center"> <a href="https://goral.net.pl/post/my-first-bread/bread3.jpg"> <img src="https://goral.net.pl/post/my-first-bread/bread3.jpg" alt="Half-baked bread inside an oven" /> </a> <figcaption><p>Bread in the middle of baking.</p></figcaption> </figure> <figure class="center"> <a href="https://goral.net.pl/post/my-first-bread/bread4.jpg"> <img src="https://goral.net.pl/post/my-first-bread/bread4.jpg" alt="Baked bread" /> </a> <figcaption><p>Final result.</p></figcaption> </figure> <figure class="center"> <a href="https://goral.net.pl/post/my-first-bread/bread5.jpg"> <img src="https://goral.net.pl/post/my-first-bread/bread5.jpg" alt="Baked bread" /> </a> </figure> </p> Anker SoundCore https://goral.net.pl/post/anker-soundcore/ Sat, 11 Apr 2020 00:00:00 +0000 https://goral.net.pl/post/anker-soundcore/ <p>I don&rsquo;t write reviews very often and I don&rsquo;t remember the last time I wrote anything longer than a tweet-long comment buried between other similar comments. I think they were mostly negative because that&rsquo;s what people generally do: they leave negative reviews when they&rsquo;re angry on a product or a service and want to share their anger with the whole world. Not this time.</p> <figure class="center"> <a href="https://goral.net.pl/post/anker-soundcore/anker.jpg"> <img src="https://goral.net.pl/post/anker-soundcore/anker.jpg" alt="Photo of speaker" /> </a> <figcaption><p>Front of Anker SoundCore Bluetooth Speaker</p></figcaption> </figure> <p>Sometime last year we decided, together with my wife, that we need a bluetooth speaker. We hoped to listnen to the music in the garden and in the kitchen where we have nothing else to generate background noise. We hesitated to spend a lot because we weren&rsquo;t sure if we were going to like it and use it, so we set our upper limit to 100-150 zł<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p> <p>My first thought was to buy one of JBL&rsquo;s models because I had just bought their earphones and was fairly happy with them<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. I did some research first and to my surprise most of friendly internet people agreed that there are better options in that price range and Anker SoundCore (first version) was often on top of their lists.</p> <p>Few quick messages later between me and my wife and I placed an order for our new bluetooth speaker. It arrived later that week and immediately conquered our hearts.</p> <h2 id="it-doesnt-swim">It doesn&rsquo;t swim</h2> <p>First things first: it doesn&rsquo;t swim. Every review says that. Apparently there&rsquo;s market for this feature but I never felt the urge to swim with my speaker. Maybe if I were younger or spent more time over the water&hellip;</p> <p>However, this fact has an implication: the speaker isn&rsquo;t waterproof. Which might be a problem if weather changes suddenly in the middle of BBQ party. I&rsquo;m yet to find myself in that situation, but I guess I don&rsquo;t throw enough parties.</p> <h2 id="sound-is-amazing">Sound is amazing</h2> <p>Jokes aside, Anker SoundCore isn&rsquo;t Hi-Fi, but its capabilities are truly amazing for the price and size. Think about it: it is 165 mm wide, 45 mm tall and has two 3 W speakers. How big can they be? 1&rdquo;, maybe 2&rdquo;? I wasn&rsquo;t familiar with speakers that small so I expected something slightly better than smartphone farting and SoundCore outgrew all of my expectations.</p> <p>Sound is clear and deep but lacks some details which can be heard on higher end equipment. You probably won&rsquo;t catch the sound of fingers sliding over the strings of cellos in the back of symphony orchestra. I&rsquo;m fine with that. After all we&rsquo;re talking about portable speaker, designed to accompany people on the trips and in places where would be inconvenient to use anything else. But even with these constraints in mind, it subjectively performs better than many bigger and more expensive speakers that I had encountered.</p> <p>I didn&rsquo;t expect any bass at all, but here it is. Not super deep but just enough satisfy. I thought that middles will be flat and trebles squeaky, but they&rsquo;re clear. Volume can be quite loud at maximum, but probably not enough to summon your elderly neighbours to come and knock at your door. Overall the sound experience is very pleasant.</p> <h2 id="battery-is-incredible">Battery is incredible</h2> <p>Everyone says that, but I must repeat it: battery is everlasting. Officially Anker promises 24 hours of play time, but that&rsquo;s a lie. Battery lasts much longer.</p> <p>SoundCore reports its battery status but after constantly seeing over 90% of remaining charge I started to think that it is glitched and kept recharging it anyway every few days. However, last month I won a lottery ticket to do some house painting over the weekend. So I turned on the music, as all profesional painters do, and started to paint. After 11 or 12 hours of non-stop work I discharged SoundCore to 60%. I think I literally gasped when I saw that number.</p> <h2 id="buttons-are-awful">Buttons are awful</h2> <p>Buttons are located on top of the speaker. There are 5 of them: power on/off, play/pause, volume up and down and bluetooth pairing. They have near to no tactile feedback which Anker replaced with rather shitty, cheap rubber feel. Because of that I tend to push buttons down really hard for really long time, just to be sure.</p> <p>Bluetooth and power buttons are the worst because they must be held down to activate. For example, according to the manual, to pair with a new device you have to hold down the bluetooth button for over a second. I never held it shorter than at least 4 seconds simply because I don&rsquo;t trust it. Same with power button - it must be held down to turn the device off. At least in this case we get a clear feedback that it worked (music stops playing) so it can be released as soona as possible.</p> <h2 id="soundcore-is-being-replaced-with-inferior-brother">SoundCore is being replaced with inferior brother</h2> <p>Since we bought it, Anker released second version of SoundCore which is arguably prettier and waterproof (finally, yay!). Too bad for Anker, it also gathered much more negative reviews for its sound<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>. It seems that you can&rsquo;t have everything: speakers either swim or sound good.</p> <p>If you&rsquo;re thinking about buying a small and cheap bluetooth speaker, this might be the good time, when new SoundCores are still available online. I love mine and wouldn&rsquo;t replace it with any other speaker. Even my 3.5 years old son demands it loudably when he plays music from my wife&rsquo;s iPod, and he&rsquo;s the most sincere reviewer.</p> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p>$30-$40 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>Since then I have bought Shure SE215 IEM and quickly learned how completely clueless I was about earphones quality. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:3" role="doc-endnote"> <p>I never touched it so I can neither confirm nor deny them. <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section> Working on TWC Formatting https://goral.net.pl/post/twc-formatting/ Wed, 01 Apr 2020 00:00:00 +0000 https://goral.net.pl/post/twc-formatting/ <p>Last year I started writing <a href="https://gitlab.com/mgoral/twc">TWC</a>, a TUI wrapper for TaskWarrior, great command line todo manager. I wrote it because I couldn&rsquo;t find any similar interactive applications and because I really like Taskwarrior, but quite dislike its tabular form of tasks presentation.</p> <p>TWC groups tasks in agendas. They are basically task filters displayed together on a single page/tab. I called each of them a <em>block</em>. Each block has separate definition of how it should render tasks on the screen. This definition is called <em>formatting string</em>.</p> <h2 id="html-era">HTML Era</h2> <p>Prior to 0.9 release users defined formatting strings as a mixture of HTML-like markup and <a href="https://docs.python.org/3/library/string.html#formatspec">Python format mini-language</a>. TWC allowed passing two kinds of HTML tags:</p> <ul> <li>definitions of styles (like background and foreground colors);</li> <li>pre-defined tags with special semantics of adding or changing text wrapped by them: <ul> <li><code>&lt;sr&gt;</code> to surround non-empty text;</li> <li><code>&lt;ind&gt;</code> to change non-empty text to some other text (indicator).</li> </ul> </li> </ul> <p>Below is an example of such string, prettified for the needs of this article. In real life it would be wrapped in quotes, assigned to the variable and with line endings escaped.</p> <div class="highlight"><pre class="chroma"><code class="language-html" data-lang="html"><span class="p">&lt;</span><span class="nt">comment</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">sr</span> <span class="na">right</span><span class="o">=</span><span class="s">&#34; &#34;</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">sr</span> <span class="na">left</span><span class="o">=</span><span class="s">&#34;[&#34;</span> <span class="na">right</span><span class="o">=</span><span class="s">&#34;]&#34;</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">ind</span> <span class="na">value</span><span class="o">=</span><span class="s">&#34;A&#34;</span><span class="p">&gt;</span>{annotations}<span class="p">&lt;/</span><span class="nt">ind</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">ind</span> <span class="na">value</span><span class="o">=</span><span class="s">&#34;D&#34;</span><span class="p">&gt;</span>{due}<span class="p">&lt;/</span><span class="nt">ind</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">ind</span> <span class="na">value</span><span class="o">=</span><span class="s">&#34;S&#34;</span><span class="p">&gt;</span>{scheduled}<span class="p">&lt;/</span><span class="nt">ind</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">sr</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">sr</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">sr</span> <span class="na">right</span><span class="o">=</span><span class="s">&#34; &#34;</span><span class="p">&gt;</span>{id}<span class="p">&lt;/</span><span class="nt">sr</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">comment</span><span class="p">&gt;</span> {description} <span class="p">&lt;</span><span class="nt">sr</span> <span class="na">left</span><span class="o">=</span><span class="s">&#34; &#34;</span><span class="p">&gt;&lt;</span><span class="nt">info</span><span class="p">&gt;</span>{tags}<span class="p">&lt;/</span><span class="nt">info</span><span class="p">&gt;&lt;/</span><span class="nt">sr</span><span class="p">&gt;</span> </code></pre></div><p>It should display something like <code>[AD] 5 Paint the room @home:errands</code>, but of course it didn&rsquo;t because HTML parser was buggy. We&rsquo;ll get back to this later.</p> <h2 id="replacing-html-with-dsl">Replacing HTML with DSL</h2> <p>TWC 0.9 introduced a huge change: instead of HTML users can pass a formatted list of items.</p> <p>Single item now looks like this: <code>name:style:fmt:</code>. with <em>style</em> and <em>fmt</em> both being optionals. Multiple items are separated by a comma or plus sign. Choice of separator is important because they have different meanings:</p> <ul> <li><code>,</code> produces a space between two items;</li> <li><code>+</code> glues items together.</li> </ul> <p>Users can pass item&rsquo;s style name after a colon and Python formatting after another one. Similar to sed, style and formatting strings must be terminated with a colon. This one is the biggest quirk which I couldn&rsquo;t get rid of.</p> <p>And here&rsquo;s an example of items definition that actually displays what we intended to with that overly verbose HTML-like thing before:</p> <div class="highlight"><pre class="chroma"><code class="language-txt" data-lang="txt">[flags:comment:%a%d%s:],id:comment:,description,tags:info: </code></pre></div><h2 id="why-not-html">Why not HTML</h2> <p>HTML formatting was single thing which I disliked the most in TWC. So I finally gave it a little thought and after few days concluded that HTML might not be the best choice for TWC formatting strings.</p> <p>User-wise it&rsquo;s hard to read and reason about because of its verbose markup. Configuration files looked horrible because of it. <code>&lt;sr&gt;</code> tag was the worst. Because it had <em>surround-if-not-empty</em> semantics, it was actually the only way to conditionally separate optionally empty items, which in turn made each each formatting string a never-ending river of <code>&lt;sr&gt;'s</code>. Because many blocks usually need slightly different formatting, this horrible pattern was repeated over and over again. Add completely non-standard HTML tags, unfamiliar to the users who now have to constantly check documentation (I, the author, had to check it from time to time) and disaster is ready.</p> <p>Code-wise situation was not much better. It required HTML parser. This alone should say everything, but let&rsquo;s elaborate on that thought, shall we?</p> <p>Parser was implemented on top of <a href="https://docs.python.org/3.5/library/html.parser.html">html.parser</a> module. It wasn&rsquo;t particularily long and complex, its test suite was more than satisfactionary, to the point that I was quite confident modifying it. It covered many uttely stupid corner cases, but that&rsquo;s fine as long as they&rsquo;re tested. And yet, after 2 days of work I somehow wasn&rsquo;t able to fix one silly bug with nesting of particular tags.</p> <p>See, HTML parser had to do a little more than just parse HTML. It had to parse it in particular way, compatible with <em>formatted text</em>, which is data format understood by <a href="https://github.com/prompt-toolkit/python-prompt-toolkit">Python Prompt Toolkit</a>, a library which TWC uses for drawing of the interface. Basically it&rsquo;s a list of tuples, but it must be kept in rather simple, flat form, because otherwise Prompt Toolkit fails to render it miserably.</p> <p>So what could I do with a DOM tree which is nested by definition? I defined each tag as a Python class that, when it was appropriate, received parts of <em>formatted text</em>, which could be modified, replaced and recreated. To keep things simple each such entity had only a limited knowledge of a small part of formatting string and nothing else. So it had to deduct what to do from a presence (or lack of presence) of arbitrary strings and from a number of tuples it received &mdash; all of that to flatten some HTML. No surprise that this is the part where bugs occured.</p> <h2 id="alternative">Alternative</h2> <p>So the alternative to HTML which I figured out is a custom domain-specific language. It was designed from scratch<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> with ease of use in mind. It&rsquo;s, I think, opinionated (a popular trend that I observe recently which stands in opposition to generic, but complex programs):</p> <ul> <li>it deliberately drops some of the features of HTML like tags nesting or allowing drop-in style definitions and replaces them with simpler, more limited counterparts;</li> <li>it also creates some arbitrary conventions like removing spaces between empty items.</li> </ul> <p>But even though, it remains quite fleximple without losing readability. At least it&rsquo;s more readable than HTML counterpart, I think that we all agree on that. A little writing gives a good effect and actually invites users to experiment with it. Users shouldn&rsquo;t need the documentation to jump in.</p> <p>Code-wise it&rsquo;s implemented on top of a simple regular expression and even simpler tokenization. You know: split on commas, partition on colons&hellip; Its simplicity works not only for end-users, but also for implementation. There are not many things to break. That&rsquo;s another win.</p> <p>Is it perfect? No. It still has a field for improvements. For example I&rsquo;d like to implement a kind of &ldquo;intelligent&rdquo; mistake fixing, especially for missing terminating colon - a common mistake I make all the time. I tried to fix it before the release, but I failed, as lack of that colon introduces many interesting corner cases. Like, what about datetime string formatting which typically uses a colon to separate hours from minutes (<code>due::%Y-%m-%d %H:%M:</code>)? What about spaces? What surrounding characters?</p> <p>I hope to figure it out one day. End of story.</p> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p>But influenced by <a href="https://weechat.org/">Weechat</a>. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section> I Blocked Bots https://goral.net.pl/post/i-blocked-bots/ Fri, 28 Feb 2020 00:00:00 +0000 https://goral.net.pl/post/i-blocked-bots/ <p>Yesterday I started blocking crawlers via robots.txt. I was forced to because it started getting out of hand. I&rsquo;ll observe traffic for a few days and will block them on level of HTTP server or fail2ban/iptables if it turns out that they don&rsquo;t respect robots.txt, contrary to the claims of the biggest offenders.</p> <p>This is mostly low-profile site. Me, my family and maybe some of my friends are doing at most 500-600 requests a day, a little more if I&rsquo;m tinkering with server.</p> <figure class="center"> <a href="https://goral.net.pl/post/i-blocked-bots/bots-goaccess.png"> <img src="https://goral.net.pl/post/i-blocked-bots/bots-goaccess.png" alt="Screenshot of chart" /> </a> <figcaption><p>14 days goaccess log</p></figcaption> </figure> <p>There are 2 main contributors to the increased traffic: <a href="https://mj12bot.com/">MJ12</a> and <a href="http://semrush.com/">SemRush</a>. Above is part of goaccess&rsquo; report from last 14 days. It is clear that these 2 bots alone do over 11000 requests a day, which is insane for a site known by maybe 10 people on Earth. It&rsquo;s nowhere near the capacity of the server, but it translates to 2 GB of transferred data every month. Wasted 2 GB, because those bots give me nothing. I don&rsquo;t even think I&rsquo;m serving that many pages in total, including some dynamically generated.</p> <p>Cherry on top: I&rsquo;m not the only one who hates these bots. <a href="https://en.wikipedia.org/robots.txt">Wikipedia</a> hates MJ12 especially as well. And <a href="http://boston.conman.org/2019/07/09-12">others</a> <a href="https://news.ycombinator.com/item?id=20453189">too</a>.</p> <h2 id="update-2020-03-03">Update: 2020-03-03</h2> <figure class="center"> <a href="https://goral.net.pl/post/i-blocked-bots/bots-aftermath.png"> <img src="https://goral.net.pl/post/i-blocked-bots/bots-aftermath.png" alt="Screenshot of chart" /> </a> </figure> <p>After few days it seems that robots.txt trick worked as things calmed down. I had to add robots.txt to every subdomain though, some disallowing all bots and some merely delaying requests (via non-standard <em>Crawl-Delay</em> directive). Still, there are more requests than I&rsquo;d expect, but I have to evaluate those before saying anything else.</p> Synchronization https://goral.net.pl/post/sync/ Sat, 15 Feb 2020 00:00:00 +0000 https://goral.net.pl/post/sync/ <p>I synchronize a lot of things. My personal knowledge base, calendar (via vdirsyncer), Qalculate exchange rates, ledger and budget repository, dotfiles, e-mail, tasks&hellip; How do I keep up with all of those things?</p> <p>As a systemd user, it&rsquo;s natural for me to set up systemd timers for some of those repetitive tasks. Unfortunately creating a timer for each small task is quite frustrating due to very explicit nature of system timers architecture: one have to create a service unit, timer unit, connect them, enable them, start them, think about their dependencies&hellip; That&rsquo;s too much work!</p> <p>So I created a single shell script (and also set up systemd timer for it) which launches all registered sync functions in parallel, with help of great <a href="https://www.gnu.org/software/parallel/">GNU Parallel</a>. It looks like this:</p> <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/bin/bash </span><span class="cp"></span> sync_cal<span class="o">()</span> <span class="o">{</span> <span class="nb">echo</span> <span class="s2">&#34;----- [ </span><span class="si">${</span><span class="nv">FUNCNAME</span><span class="p">[0]</span><span class="si">}</span><span class="s2"> ] -----&#34;</span> <span class="nb">set</span> -e vdirsyncer sync khal2rem -o <span class="nv">$HOME</span>/.khal.rem touch ~/.reminders <span class="nb">set</span> +e <span class="o">}</span> sync_qalc<span class="o">()</span> <span class="o">{</span> <span class="nb">echo</span> <span class="s2">&#34;----- [ </span><span class="si">${</span><span class="nv">FUNCNAME</span><span class="p">[0]</span><span class="si">}</span><span class="s2"> ] -----&#34;</span> <span class="nb">set</span> -e qalc &lt; &lt;<span class="o">(</span><span class="nb">echo</span> <span class="s2">&#34;exrates&#34;</span><span class="o">)</span> <span class="nb">set</span> +e <span class="o">}</span> sync_tasks<span class="o">()</span> <span class="o">{</span> <span class="nb">echo</span> <span class="s2">&#34;----- [ </span><span class="si">${</span><span class="nv">FUNCNAME</span><span class="p">[0]</span><span class="si">}</span><span class="s2"> ] -----&#34;</span> <span class="nb">set</span> -e <span class="nb">local</span> <span class="nv">h</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>hostname<span class="k">)</span><span class="s2">&#34;</span> <span class="k">if</span> <span class="o">[[</span> <span class="s2">&#34;</span><span class="nv">$h</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;hostname_behind_a_proxy&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span> proxychains task sync <span class="k">else</span> task sync <span class="k">fi</span> <span class="nb">set</span> +e <span class="o">}</span> <span class="nv">fns</span><span class="o">=()</span> register<span class="o">()</span> <span class="o">{</span> <span class="nb">export</span> -f <span class="nv">$1</span> <span class="nv">fns</span><span class="o">+=(</span><span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span><span class="o">)</span> <span class="o">}</span> register sync_cal register sync_qalc register sync_tasks <span class="c1"># Handle arguments passed to the script</span> <span class="k">if</span> <span class="o">[[</span> <span class="nv">$#</span> -gt <span class="m">0</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span> <span class="nv">fns</span><span class="o">=()</span> <span class="k">for</span> fn in <span class="s2">&#34;</span><span class="nv">$@</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">do</span> <span class="nv">fns</span><span class="o">+=(</span><span class="s2">&#34;sync_</span><span class="si">${</span><span class="nv">fn</span><span class="si">}</span><span class="s2">&#34;</span><span class="o">)</span> <span class="k">done</span> <span class="k">fi</span> parallel -j0 <span class="s1">&#39;{}&#39;</span> ::: <span class="s2">&#34;</span><span class="si">${</span><span class="nv">fns</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span> </code></pre></div><p>I called it <em>mg-sync</em> (I prefix all of my personal scripts with <em>mg-</em>, which makes them easier to find with shell tab-completion. Recently I even wrote a wrapper which instead of e.g. <em>mg-sync</em> allows me to write <em>mg sync</em>, the same way as e.g. Git works). Thanks to this script I can run in parallel all registered functions (when it&rsquo;s called with no arguments) or only selected ones (when it&rsquo;s called with arguments).</p> <p>Even though it&rsquo;s short and concise, there are few things to remember. First, each Bash function must be exported (<code>export -f</code>), otherwise it will be invisible to Parallel. Second, GNU Parallel spawns a separate job for each function (<code>-j0</code>), where full command of each job is a single argument from the argument list following triple colon. I am also explicit about command substitution (<code>{}</code>), but Parallel would work even without it: <code>parallel -j0 ::: &quot;${fns[@]}&quot;</code>.</p> <p>I like this a lot because it handles parallelism for me, waits for each ran command and correctly reports command errors. My systemd timer runs this script every few hours, but I configured a shortcut key which starts systemd service immediately if I need to quickly synchronize some data.</p> Cross References in Notes https://goral.net.pl/post/notes-2/ Wed, 12 Feb 2020 00:00:00 +0000 https://goral.net.pl/post/notes-2/ <p>My <a href="https://goral.net.pl/post/notes/">notes</a> are continuous struggle. Because I render my notes with Hugo, which is a fabulous static site generator, I kept links in Hugo-friendly form: <code>[foo]({{&lt; ref &quot;bar.md&quot; &gt;}})</code>. Unfortunately this is only understood by Hugo and sometimes I want to read and navigate notes from my phone. But fear no more - I fixed it!</p> <p>I create Makefiles for almost all of my projects. They&rsquo;re great: they provide easy to remember facade to complex tasks and have shell completion for their targets, so choosing what to run usually boils down to a little tabbing through the list. How does it relates to having and not having Hugo-like references? What should we do to have our cake and eat it? Add a little preprocessing in Makefile of course!</p> <p>First of all I needed to convert back all of my Hugo-like refs to ordinary markdown links. I used some magic sed for it which worked for ~95% of all original refs the rest I fixed manually. It was one time thing and I don&rsquo;t remember the exact regular expression unfortunately, but it was something like <code>sed -i -E -e 's/{{&lt; ref &quot;(.+.md)&quot; &gt;}}/\1/g'</code>. Unhandled cases? <code>relref</code> shortcode (obviously, I just HAD to use it at least once somewhere) and several refs in a single line (<code>.+</code> is a greedy match).</p> <p>Next, I wanted Hugo to understand that any links to <em>.md</em> files are in fact cross-references, which means that we must somehow replace bare file name with a shortcode, without changing &ndash; without even touching a working copy (because who likes when his text editor screams at you that file has changed when it hasn&rsquo;t).</p> <p>First I thought that I&rsquo;ll be able to use these fancy <a href="https://gohugo.io/hugo-pipes/">pipelines</a> which Hugo uses to pre-process assets, but apparently they&rsquo;re special snowflakes, specialized for minifying Javascript and compiling SASS to CSS. Not good. So I decided to do the simplest thing ever: copy the whole content aside, run sed through it and use it to generate my site.</p> <div class="highlight"><pre class="chroma"><code class="language-make" data-lang="make"><span class="nv">PUBDIR</span> <span class="o">:=</span> /var/www/docs <span class="nv">CONTENT_TMP</span> <span class="o">:=</span> /tmp/docs_content_tmp <span class="nf">build</span><span class="o">:</span> @rm -rf <span class="s2">&#34;</span><span class="k">$(</span>PUBDIR<span class="k">)</span><span class="s2">&#34;</span> @mkdir -p <span class="s2">&#34;</span><span class="k">$(</span>PUBDIR<span class="k">)</span><span class="s2">&#34;</span> @rm -rf <span class="s2">&#34;</span><span class="k">$(</span>CONTENT_TMP<span class="k">)</span><span class="s2">&#34;</span> cp -r content <span class="s2">&#34;</span><span class="k">$(</span>CONTENT_TMP<span class="k">)</span><span class="s2">&#34;</span> sed -i -E<span class="se">\ </span><span class="se"></span> -e <span class="s1">&#39;s,]\(([^).]+.md)\),]({{&lt; ref &#34;\1&#34; &gt;}}),g&#39;</span> <span class="se">\ </span><span class="se"></span> -e <span class="s1">&#39;s,]\((files/[^)]+)\),]({{&lt; file &#34;\1&#34; &gt;}}),g&#39;</span> <span class="se">\ </span><span class="se"></span> <span class="sb">`</span>find <span class="s2">&#34;</span><span class="k">$(</span>CONTENT_TMP<span class="k">)</span><span class="s2">&#34;</span> -name <span class="s2">&#34;*.md&#34;</span> -type f<span class="sb">`</span> hugo -d <span class="s2">&#34;</span><span class="k">$(</span>PUBDIR<span class="k">)</span><span class="s2">&#34;</span> -c <span class="s2">&#34;</span><span class="k">$(</span>CONTENT_TMP<span class="k">)</span><span class="s2">&#34;</span> @rm -rf <span class="s2">&#34;</span><span class="k">$(</span>CONTENT_TMP<span class="k">)</span><span class="s2">&#34;</span> <span class="nf">.PHONY</span><span class="o">:</span> <span class="n">build</span> </code></pre></div><p>I hate that sed, but it does the job and probably it&rsquo;s not the worst one I&rsquo;ve ever written. And hey, it even works for several links in a single line, which wasn&rsquo;t that trivial (see that <code>[^)]</code> block? That&rsquo;s a hack for catching all unicode characters in a non-greedy way, which is generally unsupported by BRE and ERE used by GNU tools. Or it depends on locale. Or something else obscure enough that I don&rsquo;t want to think about it).</p> <p>Second <code>-e</code> script additionally detects file attachments, which are stored inside each repo in <em>files</em> directory, and wraps them with a custom <code>{{&lt; file &gt;}}</code> shortcode which handles conversions to absolute paths. So for example image <code>![foo](files/bar.jpg)</code> gets converted to <code>![foo]({{&lt; &quot;files/bar.jpg&quot; &gt;}})</code>, which in turn is generated to <code>&lt;img src=&quot;http://localhost/docs/0/files/bar.jpg&quot; /&gt;</code>.</p> <p>Shortcode implementation is one-liner:</p> <div class="highlight"><pre class="chroma"><code class="language-txt" data-lang="txt">{{-/* printf &#34;%s/%s/%s&#34; .Site.BaseURL .Page.Section (.Get 0) */-}} </code></pre></div><p>I like this approach a lot because finally content of my notes is independent from any external system/renderer. And now they work rather nicely with <a href="https://github.com/gsantner/markor">Markor</a>.</p> <p>One thing which I&rsquo;ll for sure improve in future: I won&rsquo;t blindly copy the <em>whole</em> content directory, but only Markdown files. All other files (&ldquo;attachments&rdquo;) will be symlinked. It should save a little disk space and a little time spent on unnecessary copying.</p>