<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><description>Articles from the &quot;Zubashev Stepan's blog&quot;</description><atom:link href="https://faiwer.ru/rss" rel="self" type="application/rss+xml"></atom:link><link>https://faiwer.ru</link><title>Zubashev Stepan's blog</title><language>en</language><item><title>Why do I hate 2FA?</title><pubDate>Sat, 17 Jan 2026 17:51:00 +0000</pubDate><link>https://faiwer.ru/content/214-why-do-i-hate-2fa</link><guid isPermaLink="false">article-214</guid><description><![CDATA[<p>Because it’s too tightly bound to a mobile device. It seems I’ve lost access to one of my email accounts for an indefinite amount of time. I’ll skip the name of the service, it’s not very important. Today I tried to log in and failed because of their 2FA.</p>
<ul>
<li>Their mobile app somehow lost my account. That sucks.</li>
<li>I still know the password, I can’t log in without going through a recovery process.</li>
<li>Their recovery process requires access to my old SIM card.</li>
<li>Luckily I have it and got the SMS</li>
<li>Then it asked for a PIN. I don’t have one. I assume they introduced PINs after I set up my 2FA account. Fine, I can skip this step.</li>
<li>Next, I had to answer a default security question like “What’s the name of the street …”. I know the answer, but I don’t know whether it’s case-sensitive.</li>
<li>Anyway,The system refuses to accept any of my attempts.</li>
<li>Now I have to go through a long process of convincing their security team that it’s actually me.</li>
<li>I’m not sure it’ll work out. I hope as the last hope I’ll be able to send them my passport data</li>
</ul>
<p>So, why do I hate 2FA? Because it’s supposed to be a security measure. But instead it’s almost always a suffering measure.</p>
]]></description></item><item><title>Lazy &amp; Suspense in React</title><pubDate>Fri, 16 Jan 2026 10:35:00 +0000</pubDate><link>https://faiwer.ru/content/213-lazy-suspense-in-react</link><guid isPermaLink="false">article-213</guid><description><![CDATA[<p>React documentation says:</p>
<blockquote>
<p>Now that your component’s code loads on demand, you also <strong>need to</strong> specify what should be displayed while it is loading. You can do this by wrapping the lazy component or any of its parents into a boundary</p>
<ul>
<li><a target="_blank" rel="noopener noreferrer" href="https://react.dev/reference/react/lazy">Lazy - React</a></li>
</ul>
</blockquote>
<p>“Need to”? Or “must to”?</p>
<p>If you try to use <code>lazy</code> without <code>&lt;Suspense/&gt;</code> it… It works. Wow. So, is it optional?</p>
<p><strong>HELL NO</strong>! You must wrap your lazy components with <code>&lt;Suspense/&gt;</code>.</p>
<p>Why? Because such non-connected components can put React’s reconciliation engine into a semi-broken, hung state where behavior becomes unpredictable. It gets auto-healed only when the lazy promise is fulfilled.</p>
<p>So, if you don’t want to play hide &amp; seek in a mine field — do it. Wrap it with <code>&lt;Suspense/&gt;</code> even if you don’t plan to render any fallback JSX.</p>
]]></description></item><item><title>Math namespace in HTML</title><pubDate>Mon, 24 Nov 2025 21:48:00 +0000</pubDate><link>https://faiwer.ru/content/212-math-namespace-in-html</link><guid isPermaLink="false">article-212</guid><description><![CDATA[<p>TIL: HTML partially supports math:</p>
<!-- HTML --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">math</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">  &lt;</span><span style="color:#f2777a;">mrow</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">    &lt;</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;a&lt;/</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">    &lt;</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;=&lt;/</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">    &lt;</span><span style="color:#f2777a;">mfrac</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">      &lt;</span><span style="color:#f2777a;">mrow</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">        &lt;</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;-&lt;/</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">        &lt;</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;b&lt;/</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">        &lt;</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;±&lt;/</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">        &lt;</span><span style="color:#f2777a;">msqrt</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">          &lt;</span><span style="color:#f2777a;">msup</span><span style="color:#d3d0c8;">&gt;&lt;</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;b&lt;/</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;&lt;</span><span style="color:#f2777a;">mn</span><span style="color:#d3d0c8;">&gt;2&lt;/</span><span style="color:#f2777a;">mn</span><span style="color:#d3d0c8;">&gt;&lt;/</span><span style="color:#f2777a;">msup</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">          &lt;</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;-&lt;/</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">          &lt;</span><span style="color:#f2777a;">mn</span><span style="color:#d3d0c8;">&gt;4&lt;/</span><span style="color:#f2777a;">mn</span><span style="color:#d3d0c8;">&gt;&lt;</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;a&lt;/</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;&lt;</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;c&lt;/</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">        &lt;/</span><span style="color:#f2777a;">msqrt</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">      &lt;/</span><span style="color:#f2777a;">mrow</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">      &lt;</span><span style="color:#f2777a;">mrow</span><span style="color:#d3d0c8;">&gt;&lt;</span><span style="color:#f2777a;">mn</span><span style="color:#d3d0c8;">&gt;2&lt;/</span><span style="color:#f2777a;">mn</span><span style="color:#d3d0c8;">&gt;&lt;</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;a&lt;/</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;&lt;/</span><span style="color:#f2777a;">mrow</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">    &lt;/</span><span style="color:#f2777a;">mfrac</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">  &lt;/</span><span style="color:#f2777a;">mrow</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">&lt;/</span><span style="color:#f2777a;">math</span><span style="color:#d3d0c8;">&gt;
</span></pre>
<p>^ It’s a valid HTML code. It renders this way:</p>
<p><img src="https://faiwer.ru/static/upload/images/004.webp" alt=""></p>
<p>No 3rd party libs needed.</p>
]]></description></item><item><title>Parametrized var-functions</title><pubDate>Tue, 11 Nov 2025 18:49:00 +0000</pubDate><link>https://faiwer.ru/content/211-parametrized-var-functions</link><guid isPermaLink="false">article-211</guid><description><![CDATA[<!-- TypeScriptReact --><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">type </span><span style="color:#d3d0c8;">Fn = &lt;T&gt;(</span><span style="color:#f2777a;">a</span><span style="color:#d3d0c8;">: T) </span><span style="color:#cc99cc;">=&gt; </span><span style="color:#d3d0c8;">T;
</span><span style="color:#cc99cc;">declare const </span><span style="color:#f2777a;">fn</span><span style="color:#d3d0c8;">: Fn;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">const </span><span style="color:#f2777a;">a </span><span style="color:#d3d0c8;">= </span><span style="color:#f2777a;">fn</span><span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">number</span><span style="color:#d3d0c8;">&gt;; </span><span style="color:#747369;">// no &quot;()&quot;
</span><span style="color:#f2777a;">a</span><span style="color:#d3d0c8;">; </span><span style="color:#747369;">// const a: (a: number) =&gt; number
</span></pre>
<p>It was on the surface all along, but it eluded me.</p>
]]></description></item><item><title>Pure functions</title><pubDate>Thu, 06 Nov 2025 01:38:00 +0000</pubDate><link>https://faiwer.ru/content/210-pure-functions</link><guid isPermaLink="false">article-210</guid><description><![CDATA[<p>A little rant about functional programming and pure functions. There is a common delusion about pure functions. People often think that:</p>
<ul>
<li>Pure functions cannot use variables. Only constants.</li>
<li>Pure functions cannot run impure functions. Only pure.</li>
</ul>
<p>These statements make some point. But they are not entirely true. Let me clarify this. The only restrictions that pure functions have are:</p>
<ul>
<li>It must be deterministic, meaning it doesn’t have random behavior. No matter how many times you run it with the same set of arguments — you’ll get the same result.</li>
<li>It shouldn’t have any side effects.</li>
</ul>
<p>What is a side effect? Basically, it’s any change in the world outside of the function. E.g.:</p>
<ul>
<li>Changing the given argument</li>
<li>Changing a variable defined outside of the function</li>
<li>Mutating any value reachable from outside the function (including parameters) or any value that escapes the function</li>
<li>Making a query (database, network, etc.)</li>
<li>Reading a file, writing to a file, removing a file, etc.</li>
<li>Reading external mutable data</li>
<li>etc.</li>
</ul>
<p>I think you got the concept. Now let’s focus on the word “side” in “side effects”. “Side” means it’s outside of the given function. We don’t have these restrictions for internal state. Yep, a pure function may have mutable internal state.</p>
<!-- TypeScriptReact --><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">function </span><span style="color:#6699cc;">pure</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">a</span><span style="color:#d3d0c8;">: number, </span><span style="color:#f2777a;">b</span><span style="color:#d3d0c8;">: number): number {
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">let </span><span style="color:#f2777a;">c </span><span style="color:#d3d0c8;">= </span><span style="color:#f2777a;">a </span><span style="color:#d3d0c8;">** </span><span style="color:#f2777a;">b</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">c </span><span style="color:#d3d0c8;">&gt; </span><span style="color:#f99157;">100</span><span style="color:#d3d0c8;">) </span><span style="color:#f2777a;">c </span><span style="color:#d3d0c8;">%= </span><span style="color:#f99157;">100</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">return </span><span style="color:#f2777a;">c </span><span style="color:#d3d0c8;">- </span><span style="color:#f99157;">14</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">}
</span></pre>
<p>^ this one is pure, even though it mutates <code>c</code>. Just because <code>c</code> is internal to the function — it doesn’t affect or depend on anything outside. We don’t mutate it after we return the value.</p>
<p>Ok, what about running impure functions? It’s a dangerous zone. Because it is very dependent on how exactly impure the impure function. If it’s not deterministic, then we have no chance. So the question is about: what side effects an impure function may have while still allowing us to use it inside a pure function? The answer is: the only side effect it may have is mutating the given arguments (only if they are local to the pure function):</p>
<!-- TypeScriptReact --><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">function </span><span style="color:#6699cc;">impure</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">packet</span><span style="color:#d3d0c8;">: Packet): void {
</span><span style="color:#d3d0c8;">  </span><span style="color:#747369;">// deterministic and based only on packet
</span><span style="color:#d3d0c8;">  </span><span style="color:#f2777a;">packet</span><span style="color:#d3d0c8;">.hash = </span><span style="color:#6699cc;">computeHash</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">packet</span><span style="color:#d3d0c8;">); 
</span><span style="color:#d3d0c8;">}
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">function </span><span style="color:#6699cc;">pure</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">id</span><span style="color:#d3d0c8;">: number, </span><span style="color:#f2777a;">data</span><span style="color:#d3d0c8;">: Data): Packet {
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">const </span><span style="color:#f2777a;">packet</span><span style="color:#d3d0c8;">: Packet = { </span><span style="color:#f2777a;">id</span><span style="color:#d3d0c8;">, </span><span style="color:#f2777a;">data </span><span style="color:#d3d0c8;">};
</span><span style="color:#d3d0c8;">  </span><span style="color:#6699cc;">impure</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">packet</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">return </span><span style="color:#f2777a;">packet</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">}
</span></pre>
<p>Here we clearly run <code>impure</code> in <code>pure</code>. But since <code>impure</code> changes only <code>packet</code>, which is entirely managed by <code>pure</code>, we’re safe.</p>
<p>That’s it.</p>
]]><p><a link="https://faiwer.ru/content/210-pure-functions">Read more</a></p></description></item><item><title>TIL: HTMLCollection is alive</title><pubDate>Sun, 19 Oct 2025 14:52:00 +0000</pubDate><link>https://faiwer.ru/content/209-til-htmlcollection-is-alive</link><guid isPermaLink="false">article-209</guid><description><![CDATA[<p>TIL: <code>HTMLCollection</code>s are live-collections. What does it mean? Look. Imagine you have HTML like this:</p>
<!-- HTML --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">div</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">  &lt;</span><span style="color:#f2777a;">br</span><span style="color:#d3d0c8;">/&gt;
</span><span style="color:#d3d0c8;">  &lt;</span><span style="color:#f2777a;">br</span><span style="color:#d3d0c8;">/&gt;
</span><span style="color:#d3d0c8;">&lt;/</span><span style="color:#f2777a;">div</span><span style="color:#d3d0c8;">&gt;
</span></pre>
<p>Then you want to find all its <code>&lt;br/&gt;</code>s:</p>
<!-- TypeScriptReact --><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">const </span><span style="color:#f2777a;">collection  </span><span style="color:#d3d0c8;">= </span><span style="color:#f2777a;">$0</span><span style="color:#d3d0c8;">.</span><span style="color:#66cccc;">getElementsByTagName</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">br</span><span style="color:#d3d0c8;">&#39;);
</span></pre>
<p><img src="https://faiwer.ru/static/upload/images/002.webp" alt=""></p>
<p>Thus far, everything is obvious, right? Ok, let’s add one more <code>&lt;br/&gt;</code>:</p>
<!-- TypeScriptReact --><pre style="background-color:#2d2d2d;">
<span style="color:#f2777a;">$0</span><span style="color:#d3d0c8;">.</span><span style="color:#66cccc;">append</span><span style="color:#d3d0c8;">(document.</span><span style="color:#66cccc;">createElement</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">br</span><span style="color:#d3d0c8;">&#39;));
</span></pre>
<p>So, now have three of them. Let’s read the same <code>collection</code> element again:
<img src="https://faiwer.ru/static/upload/images/003.webp" alt=""></p>
<p>Whoa… It was updated too. Why? <code>HTMLCollection</code> is a “live” collection:</p>
<blockquote>
<p>An HTMLCollection in the HTML DOM is live; it is automatically updated when the underlying document is changed. For this reason it is a good idea to make a copy (e.g., using Array.from) to iterate over if adding, moving, or removing nodes.</p>
<ul>
<li><a target="_blank" rel="noopener noreferrer" href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection">Mozilla</a></li>
</ul>
</blockquote>
]]></description></item><item><title>CSS color filters &amp; black</title><pubDate>Mon, 29 Sep 2025 16:05:09 +0000</pubDate><link>https://faiwer.ru/content/208-css-color-filters-black</link><guid isPermaLink="false">article-208</guid><description><![CDATA[<p>I ran into an issue. Code like this:</p>
<!-- CSS --><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">.</span><span style="color:#f99157;">link</span><span style="color:#6699cc;">:</span><span style="color:#cc99cc;">hover </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;">  filter: </span><span style="color:#66cccc;">brightness</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">1.5</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">}
</span></pre>
<p>… worked perfectly on a colored icon but had no effect on a text label. It turned out that the label used the color <code>#000</code>. <code>brightness()</code> is a purely mathematical function that multiplies the R, G, and B values by the given factor. This makes no difference for pure black, since <code>0 * anything = 0</code>.</p>
]]></description></item><item><title>Evict password managers</title><pubDate>Sun, 28 Sep 2025 06:11:42 +0000</pubDate><link>https://faiwer.ru/content/207-evict-password-managers</link><guid isPermaLink="false">article-207</guid><description><![CDATA[<p>There are multiple password managers like 1pass, KeePass, and LastPass. Most of them have browser extensions. They are great. But they alter the page behavior, including HTML. Sometimes we need to prevent them from doing that.</p>
<p>Many years ago, people just did this:</p>
<!-- HTML --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">input </span><span style="color:#f99157;">autocomplete</span><span style="color:#d3d0c8;">=&quot;</span><span style="color:#99cc99;">off</span><span style="color:#d3d0c8;">&quot;
</span></pre>
<p>Now it doesn’t work. These extensions don’t respect this attribute. You might have seen this one:</p>
<!-- HTML --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">input </span><span style="color:#f99157;">data-lpignore</span><span style="color:#d3d0c8;">=&quot;</span><span style="color:#99cc99;">true</span><span style="color:#d3d0c8;">&quot;
</span></pre>
<p>It doesn’t work either (at least not with KeePass)</p>
<p>I came up with a simple and pretty silly solution:</p>
<!-- HTML --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">input </span><span style="color:#f99157;">type</span><span style="color:#d3d0c8;">=&quot;</span><span style="color:#99cc99;">text</span><span style="color:#d3d0c8;">&quot; </span><span style="color:#f99157;">onfocus</span><span style="color:#d3d0c8;">=&quot;</span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.target.type=&#39;</span><span style="color:#99cc99;">password</span><span style="color:#d3d0c8;">&#39;&quot;
</span></pre>
<p>And it works ;-) The extensions don’t care about text-inputs. And they are not smart enough to detect new controls. At least for now.</p>
<p><strong>upd</strong>: It helped with <code>KeePass</code>, but didn’t work out with <code>1Password</code>.</p>
]]></description></item><item><title>New blog</title><pubDate>Fri, 26 Sep 2025 09:36:44 +0000</pubDate><link>https://faiwer.ru/content/198-new-blog</link><guid isPermaLink="false">article-198</guid><description><![CDATA[<h2>The idea</h2>
<p>In June, I started learning <code>Rust</code>. Why? Just out of curiosity. A system-level language with a lot of syntax sugar, new patterns, and approaches. So, I started with the Rust Book, got through half of it, and decided to build something real.</p>
<p>My previous blog was made with <code>PHP 5.5</code> more than ~8 years ago. It wasn’t bad, but I completely quit PHP. I didn’t want to touch that old codebase. So I came to the conclusion: why not write a new one, this time in Rust?</p>
<p>Here it is. You’re welcome ;)</p>
<p><img src="https://faiwer.ru/static/upload/images/001.jpg" alt=""></p>
<h2>AI</h2>
<p>I didn’t want to vibe-code it. However, I didn’t want to face all the obstacles by myself. So I used <code>ChatGPT</code> a lot, for almost every aspect of the development. I turned off <code>Copilot</code> and didn’t use <code>Cursor</code>. Because I wanted to write the blog “myself”. To feel it. At least in some merit. GTP was always in a separate browser tab. More like a helper, not an author.</p>
<h2>Rust. First impression</h2>
<p>Rust is wonderful, but it’s a system-level language. I had no real experience in this area before. Just a few simple <code>C++</code> <code>LeetCode</code> problems, which gave me no real <code>C++</code> knowledge. So it was a real challenge. But I wasn’t completely unarmed. I still remembered something about pointers, the heap, allocation, and other important system-level stuff. Though it was purely theoretical in my mind.</p>
]]><p><a link="https://faiwer.ru/content/198-new-blog">Read more</a></p></description></item><item><title>Launches &amp; Landings in 2023-2025</title><pubDate>Sun, 24 Aug 2025 20:45:00 +0000</pubDate><link>https://faiwer.ru/content/199-launches-landings-in-2023-2025</link><guid isPermaLink="false">article-199</guid><description><![CDATA[<h3>My landings in 2023-2025</h3>
<p>
<iframe class="embed-youtube" src="https://www.youtube.com/embed/NJqdQ_hYIT4" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
</p>
<p>Launches are after the break</p>
]]><p><a link="https://faiwer.ru/content/199-launches-landings-in-2023-2025">Read more</a></p></description></item><item><title>Flights in France. 2025</title><pubDate>Tue, 19 Aug 2025 06:47:00 +0000</pubDate><link>https://faiwer.ru/content/200-flights-in-france-2025</link><guid isPermaLink="false">article-200</guid><description><![CDATA[<p>Four my flights from this year in France.</p>
<h3>Apotre ➔ Sisteron ➔ Laragne-Montéglin</h3>
<p>
<iframe class="embed-youtube" src="https://www.youtube.com/embed/Uwf4kIQONuM" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
</p>
<p>Three more videos are after the break</p>
<ul>
<li>Chabre ➞ Montmaur</li>
<li>Aspres → Pic de Bure → Gap</li>
<li>Sailplane flight. Aérodrome de Serres</li>
</ul>
]]><p><a link="https://faiwer.ru/content/200-flights-in-france-2025">Read more</a></p></description></item><item><title>&quot;Native code&quot; label</title><pubDate>Thu, 31 Jul 2025 01:14:00 +0000</pubDate><link>https://faiwer.ru/content/181-native-code-label</link><guid isPermaLink="false">article-181</guid><description><![CDATA[<p>How to detect if a function is a core part of browser API? This way:</p>
<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">const </span><span style="color:#f2777a;">a </span><span style="color:#d3d0c8;">= () </span><span style="color:#cc99cc;">=&gt; </span><span style="color:#d3d0c8;">{};
</span><span style="color:#d3d0c8;">  </span><span style="color:#f2777a;">a</span><span style="color:#d3d0c8;">.</span><span style="color:#66cccc;">toString</span><span style="color:#d3d0c8;">(); </span><span style="color:#747369;">// () =&gt; {}
</span><span style="color:#d3d0c8;">  console.</span><span style="color:#66cccc;">log</span><span style="color:#d3d0c8;">.</span><span style="color:#66cccc;">toString</span><span style="color:#d3d0c8;">(); </span><span style="color:#747369;">// function log() { [native code] }
</span></pre>
<p>You see? <code>[native code]</code> label. Sounds good, right?</p>
<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">const </span><span style="color:#f2777a;">a </span><span style="color:#d3d0c8;">= () </span><span style="color:#cc99cc;">=&gt; </span><span style="color:#d3d0c8;">{};
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">const </span><span style="color:#f2777a;">b </span><span style="color:#d3d0c8;">= </span><span style="color:#f2777a;">a</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">bind</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">null</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">  </span><span style="color:#f2777a;">b</span><span style="color:#d3d0c8;">.</span><span style="color:#66cccc;">toString</span><span style="color:#d3d0c8;">(); </span><span style="color:#747369;">// function () { [native code] }
</span></pre>
<p>Oops. But we still have this <code>log</code> name in the string snapshot.</p>
]]></description></item><item><title>Unternberg → Kössen → Kramsach</title><pubDate>Tue, 03 Jun 2025 10:52:49 +0000</pubDate><link>https://faiwer.ru/content/201-unternberg-k-ssen-kramsach</link><guid isPermaLink="false">article-201</guid><description><![CDATA[<p>Icaro Laminar, 2025.05.31. ~67km</p>
<p>
<iframe class="embed-youtube" src="https://www.youtube.com/embed/7f3GZBtu8IQ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
</p>
]]></description></item><item><title>Babel to EsBuild migration for Webpack</title><pubDate>Sun, 09 Feb 2025 13:00:00 +0000</pubDate><link>https://faiwer.ru/content/180-babel-to-esbuild-migration-for-webpack</link><guid isPermaLink="false">article-180</guid><description><![CDATA[<p>Recently I had some time to take a look at the performance of my pet project. One thing bothered me: the project is relatively small, but the bundle size was too big: <code>780 KiB</code>. Whereas the project had only a few dependencies: <code>react</code> (~10 KiB), <code>react-router</code> (~70 KiB), <code>react-dom</code> (~90 KiB), <code>dayjs</code> (~3 KiB), <code>lodash</code> (~70 KiB). The bundle should not be that big for so few dependencies.</p>
<p>So I ran <code>webpack-dev-analyzer</code> and found a weird thing: the sum of the libs is 3 times smaller than the final bundle size. Then I decided to take a look at <code>@statoscope/webpack-plugin</code>. It was a good tool but it gave me the same result. The bundle has way too much trash.</p>
<p>Ok. Probably it’s just webpack wrappers over 300+ small modules. %cut% So I asked <code>Perplexity</code> - what can I do with it? Turned out all the proper options were already activated. So, I made a decision - it’s time to try esbuild. It made a huge impact on my work project, so I knew it could work out.</p>
<p>I knew that <code>esbuild</code> didn’t support a lot of stuff that babel supports. So I expected that it would take a day to migrate the codebase to <code>esbuild</code>. But surprisingly once I replaced <code>babel-loader</code> with <code>esbuild-loader</code> the build command worked out. The build didn’t work but the bundle was assembled without any errors.</p>
<p>Did it help? Nope. Instead of <code>780 KiB</code>, I got <code>~1 MiB</code>. Luckily I found out that there’s also <code>EsbuildPlugin</code>, which replaces <code>TerserPlugin</code> and works better. Added this line, it worked like a charm, and… Whoa! <code>280 KiB</code>. That’s huuuge.</p>
<p>So I figured out - it worked. Now I have to find a way to fix the app (spoiler: it didn’t work at all).</p>
<h3>Implicit React imports</h3>
<p>React 17+ supports the possibility of avoiding adding <code>import React from ‘react’</code> to every TSX-file. <code>esbuild</code> supports it too, but somehow it didn’t work out of the box. Solution:</p>
<!-- Git Diff --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">  const esbuildLoader: RuleSetRule = {
</span><span style="color:#d3d0c8;">    test: /\.(js|ts|tsx)$/,
</span><span style="color:#d3d0c8;">    loader: &#39;esbuild-loader&#39;,
</span><span style="color:#d3d0c8;">    exclude: /node_modules/,
</span><span style="color:#d3d0c8;">    options: {
</span><span style="color:#d3d0c8;">       target: &#39;es2020&#39;,
</span><span style="color:#d3d0c8;">       minify: cfg.PROD,
</span><span style="color:#99cc99;">+      // Support implicit React imports:
</span><span style="color:#99cc99;">+      jsx: &#39;automatic&#39;,
</span><span style="color:#99cc99;">+      jsxFactory: &#39;React.createElement&#39;,
</span><span style="color:#99cc99;">+      jsxImportSource: &#39;react&#39;,
</span><span style="color:#d3d0c8;">    } satisfies LoaderOptions,
</span><span style="color:#d3d0c8;">  };
</span></pre>
<h3>TSX-Control-Statements</h3>
<p>I just removed the packages and all their <code>&lt;If/&gt;</code>s. I liked them though. There’s a plugin for <code>Vite</code> that adds their support. But I still use <code>Webpack</code> and the plugin works via string replacements, not via AST.</p>
<h3>SourceMaps</h3>
<p>To enable sourcemaps I had to disable <code>EsbuildPlugin</code> for development. Theoretically, it supports sourcemaps, but no matter what I did it didn’t work. I even debugged the package itself. Didn’t help. So I surrendered and enabled it only for the prod build.</p>
<h3>TypeScript</h3>
<p>EsBuild supports TypeScript syntax out of the box but it doesn’t check types. So it builds extremely fast but I need to know my errors. Solution:</p>
<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">  </span><span style="color:#f2777a;">webpackConfig</span><span style="color:#d3d0c8;">.plugins!.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(
</span><span style="color:#d3d0c8;">    new </span><span style="color:#f2777a;">ForkTsCheckerWebpackPlugin</span><span style="color:#d3d0c8;">({
</span><span style="color:#d3d0c8;">      typescript: {
</span><span style="color:#d3d0c8;">        configFile: </span><span style="color:#f2777a;">cfg</span><span style="color:#d3d0c8;">.paths.TS_CONFIG,
</span><span style="color:#d3d0c8;">      },
</span><span style="color:#d3d0c8;">    })
</span><span style="color:#d3d0c8;">  );
</span></pre>
<p>How does it work? The build and the type-checking processes are run in parallel. The 2nd doesn’t linger the 1st. You still can see the errors in the browser (and CLI) console. And visually too. Nice.</p>
<h3>ES-Lint</h3>
<p>For some reason, it stopped working. The solution was pretty simple:</p>
<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">  </span><span style="color:#f2777a;">webpackConfig</span><span style="color:#d3d0c8;">.plugins!.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(
</span><span style="color:#d3d0c8;">    new </span><span style="color:#f2777a;">ESLintPlugin</span><span style="color:#d3d0c8;">({
</span><span style="color:#d3d0c8;">      extensions: [&#39;</span><span style="color:#99cc99;">ts</span><span style="color:#d3d0c8;">&#39;, &#39;</span><span style="color:#99cc99;">tsx</span><span style="color:#d3d0c8;">&#39;],
</span></pre>
<h3>Final results</h3>
<ul>
<li>File sizes
<ul>
<li>Before: 641 KiB + 234 KiB + 29.5 KiB (css) = <code>904.5 KiB</code></li>
<li>After: 221 KiB + 55 KiB + 8.1 KiB (o_O, css) = <code>284.1 KiB</code></li>
<li>After <code>minifyIdentifiers: true</code> (scary thing) &amp; <code>minifyWhitespace: true</code>:  138 KiB + 44.2 KiB + 8.1 KiB = <code>190.1 KiB</code></li>
</ul>
</li>
<li>DOMContentLoaded:
<ul>
<li>Before: <code>737ms</code></li>
<li>After: <code>231ms</code></li>
<li>After-2: <code>131ms</code></li>
</ul>
</li>
<li>Build time: <code>esbuild</code> was ~3x faster</li>
</ul>
<h3>Afterthought</h3>
<p>Taking a deeper look I found:</p>
<ul>
<li>The main issue wasn’t in <code>babel</code>. The main issue was that I used <code>inline-source-map</code> for prod. I didn’t want to do it. Just a mistake. And both bundle analyzers didn’t notice that. Even when I manually looked at the file I barely found it (the horisontal scroll was too big). Fixing this issue dropped the file size 3x times even with <code>babel</code>.</li>
<li><code>esbuild</code> has a better wrapper logic for a ton of tiny files than <code>babel</code> does.</li>
<li>Whitespace minification is quite important. <code>GZip</code> doesn’t eliminate the difference.</li>
<li>Name mangling is a beast. But be careful, it can easily break your app in runtime.</li>
</ul>
]]><p><a link="https://faiwer.ru/content/180-babel-to-esbuild-migration-for-webpack">Read more</a></p></description></item><item><title>Flights in 2024</title><pubDate>Fri, 16 Aug 2024 08:52:00 +0000</pubDate><link>https://faiwer.ru/content/202-flights-in-2024</link><guid isPermaLink="false">article-202</guid><description><![CDATA[<h3>Unternberg → Kitzbühel</h3>
<p>
<iframe class="embed-youtube" src="https://www.youtube.com/embed/pNeiC1LBSZg" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
</p>
<p>One more video is after the break (Aspres, France)</p>
]]><p><a link="https://faiwer.ru/content/202-flights-in-2024">Read more</a></p></description></item><item><title>Flights in 2023</title><pubDate>Wed, 27 Sep 2023 10:56:00 +0000</pubDate><link>https://faiwer.ru/content/203-flights-in-2023</link><guid isPermaLink="false">article-203</guid><description><![CDATA[<h3>Walchsee. Kössen</h3>
<p>
<iframe class="embed-youtube" src="https://www.youtube.com/embed/awJSfnwp80w" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
</p>
<p>Two more videos are after the break:</p>
<ul>
<li>Kössen</li>
<li>Ruhpolding, Unternberg</li>
</ul>
]]><p><a link="https://faiwer.ru/content/203-flights-in-2023">Read more</a></p></description></item><item><title>How to migrate Javascript webpack configuration to Typescript</title><pubDate>Sat, 25 Mar 2023 14:00:00 +0000</pubDate><link>https://faiwer.ru/content/179-how-to-migrate-javascript-webpack-configuration-to-typescript</link><guid isPermaLink="false">article-179</guid><description><![CDATA[<p>It turned out that the newest versions (at least 5+) of webpack support Typescript out of the box. So the algorithm is next:</p>
<ul>
<li>Create <code>tsconfig.json</code> at the root level. Content:<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;">  &quot;</span><span style="color:#99cc99;">compilerOptions</span><span style="color:#d3d0c8;">&quot;: {
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">module</span><span style="color:#d3d0c8;">&quot;: &quot;</span><span style="color:#99cc99;">CommonJS</span><span style="color:#d3d0c8;">&quot;,
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">target</span><span style="color:#d3d0c8;">&quot;: &quot;</span><span style="color:#99cc99;">ES5</span><span style="color:#d3d0c8;">&quot;,
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">esModuleInterop</span><span style="color:#d3d0c8;">&quot;: </span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">,
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">checkJs</span><span style="color:#d3d0c8;">&quot;: </span><span style="color:#f99157;">false</span><span style="color:#d3d0c8;">,
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">strict</span><span style="color:#d3d0c8;">&quot;: </span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">,
</span><span style="color:#d3d0c8;">  }
</span><span style="color:#d3d0c8;">}
</span></pre>
</li>
<li>Rename all *<code>.js</code> files to <code>*.ts</code></li>
<li>Type all of them:
<ul>
<li>no more ugly <code>require</code>, use <code>import</code>.</li>
<li><code>webpack</code> package has typings out of the box.
<ul>
<li>You may find these types useful: <code>Configuration</code> &amp; <code>RuleSetRule</code></li>
<li>To enable <code>devServer</code> write this: <!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#f2777a;">interface </span><span style="color:#f99157;">Configuration </span><span style="color:#f2777a;">extends </span><span style="color:#f99157;">WebpackConfiguration </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;">  </span><span style="color:#f2777a;">devServer</span><span style="color:#d3d0c8;">?: </span><span style="color:#f99157;">WebpackDevServerConfiguration</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">}
</span></pre>
</li>
</ul>
</li>
<li>Some of the popular plugins have types too.</li>
<li>Some of them don’t have types at all:
<ul>
<li>Create a <code>*.d.ts</code> file</li>
<li>Put there something like this:<!-- TypeScript --><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">declare module </span><span style="color:#d3d0c8;">&#39;</span><span style="color:#99cc99;">postcss-assets</span><span style="color:#d3d0c8;">&#39; {
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">export default function </span><span style="color:#6699cc;">postcssAssets</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">opts</span><span style="color:#d3d0c8;">: {
</span><span style="color:#d3d0c8;">    </span><span style="color:#f2777a;">basePath</span><span style="color:#d3d0c8;">: string;
</span><span style="color:#d3d0c8;">    </span><span style="color:#f2777a;">relative</span><span style="color:#d3d0c8;">: boolean;
</span><span style="color:#d3d0c8;">  }): unknown;
</span><span style="color:#d3d0c8;">}
</span></pre>
</li>
</ul>
</li>
</ul>
</li>
<li>Make sure your <code>webpack.config.ts</code> file is placed at the root level. I mean exactly at the same spot where <code>node_modules</code> is. Otherwise, you won’t be able to build it. No <code>compilerOptions</code> helped me. </li>
<li>Run webpack. It should work.</li>
</ul>
]]></description></item><item><title>How to make a simple system.d service for a node.js server</title><pubDate>Tue, 21 Feb 2023 17:00:00 +0000</pubDate><link>https://faiwer.ru/content/178-how-to-make-a-simple-system-d-service-for-a-node-js-server</link><guid isPermaLink="false">article-178</guid><description><![CDATA[<p>Put this content:</p>
<!-- INI --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">[Unit]
</span><span style="color:#cc99cc;">Description</span><span style="color:#d3d0c8;">=</span><span style="color:#99cc99;">{name}
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">[Service]
</span><span style="color:#cc99cc;">Type</span><span style="color:#d3d0c8;">=</span><span style="color:#99cc99;">simple
</span><span style="color:#cc99cc;">User</span><span style="color:#d3d0c8;">=</span><span style="color:#99cc99;">{user}
</span><span style="color:#cc99cc;">ExecStart</span><span style="color:#d3d0c8;">=</span><span style="color:#99cc99;">/usr/bin/node {full-path-to-script}.js
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">[Install]
</span><span style="color:#cc99cc;">WantedBy</span><span style="color:#d3d0c8;">=</span><span style="color:#99cc99;">multi-user.target
</span></pre>
<p>… somewhere as <code>{name}.service</code>, where:</p>
<ul>
<li><code>{name}</code> is the name of the service</li>
<li><code>{user}</code> is the name of the user to run the script (optional)</li>
</ul>
<p>… then:</p>
<ul>
<li>run: <code>this sudo ln -s /{full_path}/{name}.service /lib/systemd/system/{name}.service</code> </li>
<li>then this: <code>sudo systemctl daemon-reload</code></li>
<li>then this: <code>sudo systemctl enable {name}.service</code></li>
<li>and finally this: <code>sudo systemctl start {name}</code></li>
</ul>
<p>How does it work?</p>
<ul>
<li>It starts the service on boot (see <code>WantedBy</code> section).</li>
<li>Using <code>ExecStart</code> command. Important: we specify the full path to <code>node</code></li>
<li><code>SystemD</code> remembers the <code>PID</code> of the new process and considers the service is ongoing until the process is died.</li>
<li>So any subsequent <code>systemctrl start {name}</code> won’t do anything if the previous process is alive.</li>
<li>This behavior is determined by <code>Type=Simple</code></li>
</ul>
]]></description></item><item><title>Launches &amp; Landings in 2019-2022</title><pubDate>Tue, 27 Sep 2022 08:59:00 +0000</pubDate><link>https://faiwer.ru/content/204-launches-landings-in-2019-2022</link><guid isPermaLink="false">article-204</guid><description><![CDATA[<h3>Landings</h3>
<p>
<iframe class="embed-youtube" src="https://www.youtube.com/embed/47-bkY8In8Q" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
</p>
<p>One more video (with my launches) is after the break</p>
]]><p><a link="https://faiwer.ru/content/204-launches-landings-in-2019-2022">Read more</a></p></description></item><item><title>Multilayout Keybinds in a Browser</title><pubDate>Wed, 09 Feb 2022 13:00:00 +0000</pubDate><link>https://faiwer.ru/content/177-multilayout-keybinds-in-a-browser</link><guid isPermaLink="false">article-177</guid><description><![CDATA[<p>Imagine that you want to add support of some keybinds in your web application. Let it be <code>Ctrl+L</code> for liking\unliking something. What kind of issues can you face in such a scenario?</p>
<h3>CMD or Ctrl?</h3>
<p>At first look at <code>Ctrl</code>. Probably on <code>MacOS</code> you’d like to replace it with <code>CMD</code>.  You can check it by <code>event.metaKey</code>.</p>
<h3>Extra modificators</h3>
<p>Probably you wouldn’t like to consider <code>Ctrl+Alt+S</code> as <code>Ctrl+S</code>. So don’t forget to handle this case.</p>
<h3>Different layouts</h3>
<p>Not every language that uses the Latin alphabet has the <code>L</code> button at the same position as in a typical English keyboard layout. You need to decide what is more important to you ― a real key position on a keyboard or a letter upon of it. I’d guess that the 2nd case is preferable for most applications. </p>
<p>To get a real key position you can use <code>which</code>, <code>code</code>, <code>codeKey</code> properties. To get a letter on the key use <code>key</code> property.</p>
<h3>Different alphabets</h3>
<p>What’s about Greek or Russian alphabets? Or any other possible alphabets? Or not even alphabets? There’re different strategies. And one of them is to use a key from a typical English keyboard layout. So it leads us again to <code>code</code> and <code>codeKey</code> properties.</p>
<h3>Example</h3>
<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">const </span><span style="color:#f2777a;">getEventKeyBind </span><span style="color:#d3d0c8;">= event </span><span style="color:#cc99cc;">=&gt; </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">const </span><span style="color:#f2777a;">keybind </span><span style="color:#d3d0c8;">= [];
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.metaKey) </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">cmd</span><span style="color:#d3d0c8;">&#39;);
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.ctrlKey) </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">ctrl</span><span style="color:#d3d0c8;">&#39;);
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.shiftKey) </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">shift</span><span style="color:#d3d0c8;">&#39;);
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.altKey) </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">alt</span><span style="color:#d3d0c8;">&#39;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.key === &#39; &#39;) </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">space</span><span style="color:#d3d0c8;">&#39;);
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">else </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;">    </span><span style="color:#cc99cc;">const </span><span style="color:#f2777a;">key </span><span style="color:#d3d0c8;">= </span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.key.</span><span style="color:#f2777a;">toLowerCase</span><span style="color:#d3d0c8;">();
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">    </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">key</span><span style="color:#d3d0c8;">.length !== </span><span style="color:#f99157;">1 </span><span style="color:#d3d0c8;">|| </span><span style="color:#f2777a;">key</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">match</span><span style="color:#d3d0c8;">(/</span><span style="color:#66cccc;">^[a-z]$</span><span style="color:#d3d0c8;">/)) {
</span><span style="color:#d3d0c8;">      </span><span style="color:#747369;">// latin key or a special key
</span><span style="color:#d3d0c8;">      </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">key</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">    } </span><span style="color:#cc99cc;">else </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;">      </span><span style="color:#747369;">// extra-latin or non-latin key
</span><span style="color:#d3d0c8;">      </span><span style="color:#cc99cc;">const </span><span style="color:#d3d0c8;">[, </span><span style="color:#f2777a;">enSymbol</span><span style="color:#d3d0c8;">] = </span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.code.</span><span style="color:#f2777a;">match</span><span style="color:#d3d0c8;">(/</span><span style="color:#66cccc;">^Key(\w)$</span><span style="color:#d3d0c8;">/) || [];
</span><span style="color:#d3d0c8;">      </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">enSymbol </span><span style="color:#d3d0c8;">? </span><span style="color:#f2777a;">enSymbol</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">toLowerCase</span><span style="color:#d3d0c8;">() : </span><span style="color:#f2777a;">key</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">    }
</span><span style="color:#d3d0c8;">  }
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">return </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">join</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">+</span><span style="color:#d3d0c8;">&#39;);
</span><span style="color:#d3d0c8;">};
</span></pre>
]]></description></item></channel></rss>