<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Read with AI 📖⚡️🖋️: Technical Miscellany]]></title><description><![CDATA[Bits and pieces I find while doing work]]></description><link>https://readwithai.substack.com/s/technical-miscellany</link><image><url>https://substackcdn.com/image/fetch/$s_!PXgF!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27d164e2-31fb-4b1b-a296-5ea6264ea8d0_128x128.png</url><title>Read with AI 📖⚡️🖋️: Technical Miscellany</title><link>https://readwithai.substack.com/s/technical-miscellany</link></image><generator>Substack</generator><lastBuildDate>Sun, 03 May 2026 10:43:11 GMT</lastBuildDate><atom:link href="https://readwithai.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Read with ai]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[readwithai@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[readwithai@substack.com]]></itunes:email><itunes:name><![CDATA[Read with ai]]></itunes:name></itunes:owner><itunes:author><![CDATA[Read with ai]]></itunes:author><googleplay:owner><![CDATA[readwithai@substack.com]]></googleplay:owner><googleplay:email><![CDATA[readwithai@substack.com]]></googleplay:email><googleplay:author><![CDATA[Read with ai]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Hostnames are better than ports]]></title><description><![CDATA[I am making tools to help people read deeply with the help of machines and sometimes AI.]]></description><link>https://readwithai.substack.com/p/hostnames-are-better-than-ports</link><guid isPermaLink="false">https://readwithai.substack.com/p/hostnames-are-better-than-ports</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Wed, 22 Oct 2025 21:37:35 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!PXgF!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27d164e2-31fb-4b1b-a296-5ea6264ea8d0_128x128.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p>I am making tools to help people <strong>read deeply</strong> with the help of machines and sometimes AI. If the topic seems interesting check out this subreddit on <strong><a href="https://www.reddit.com/r/machineAidedReading/">machineAidedReading</a></strong>. If you like the cut of my jib or want to follow my efforts <strong><a href="http://readwithai.substack.com">check out the blog</a></strong> or follow me on <strong><a href="http://x.com/readwithai">X</a></strong>.</p><p></p></blockquote><p>When doing development - particularly backend development or devops work you can quickly collect a plethora of ports for different things which start becoming part to remember. I used to do things like try to encode meaning into these port numbers using Lewis Carrol&#8217;s phonetic encoding systems and other similar games. So if I had a port for accounts this would map to 7210 - a(.)K(7)(ou)N(2)T(1)S(0). All kind of confusing. </p><p>But recently I discovered a fact that I should have known that the localhost host IP address 127.0.0.1 is actually part of an entire subnet 127.0.0.0/24 - all of which are looped back to your machine. This means that instead of using ports for services you can have an entire machine which can be named in /etc/hosts - on unixy machines. This means that all your services can be <strong>named.</strong></p><p>The only question is what port to use these named services. Unfortunately port 0 is reserved for special IP purposes as is port 1 - though apparently it not used that much. There are also problems with low ports being reserved by port. An issue that can be <a href="https://superuser.com/questions/710253/allow-non-root-process-to-bind-to-port-80-and-443">worked around</a> by asking the kernel to stop reserving ports - but security paranoia suggests this is a bad idea. Instead I&#8217;ve opted for using <strong>port 1024 </strong>for all services and just giving them a name in /etc/hosts.</p><p>Makes me life a bit more enjoyable!</p>]]></content:encoded></item><item><title><![CDATA[This is not a redirect URL]]></title><description><![CDATA[This is the URL is not an oAuth URL redirect.]]></description><link>https://readwithai.substack.com/p/this-is-not-a-redirect-url</link><guid isPermaLink="false">https://readwithai.substack.com/p/this-is-not-a-redirect-url</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Tue, 13 May 2025 03:00:59 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!CH11!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bd60ec3-8379-4d8a-89d1-168c485b5d4e_453x414.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em><strong>This is the URL is not an oAuth URL redirect.</strong></em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CH11!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bd60ec3-8379-4d8a-89d1-168c485b5d4e_453x414.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CH11!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bd60ec3-8379-4d8a-89d1-168c485b5d4e_453x414.png 424w, https://substackcdn.com/image/fetch/$s_!CH11!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bd60ec3-8379-4d8a-89d1-168c485b5d4e_453x414.png 848w, https://substackcdn.com/image/fetch/$s_!CH11!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bd60ec3-8379-4d8a-89d1-168c485b5d4e_453x414.png 1272w, https://substackcdn.com/image/fetch/$s_!CH11!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bd60ec3-8379-4d8a-89d1-168c485b5d4e_453x414.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CH11!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bd60ec3-8379-4d8a-89d1-168c485b5d4e_453x414.png" width="453" height="414" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6bd60ec3-8379-4d8a-89d1-168c485b5d4e_453x414.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:414,&quot;width&quot;:453,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:361714,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://readwithai.substack.com/i/163447556?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bd60ec3-8379-4d8a-89d1-168c485b5d4e_453x414.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CH11!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bd60ec3-8379-4d8a-89d1-168c485b5d4e_453x414.png 424w, https://substackcdn.com/image/fetch/$s_!CH11!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bd60ec3-8379-4d8a-89d1-168c485b5d4e_453x414.png 848w, https://substackcdn.com/image/fetch/$s_!CH11!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bd60ec3-8379-4d8a-89d1-168c485b5d4e_453x414.png 1272w, https://substackcdn.com/image/fetch/$s_!CH11!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bd60ec3-8379-4d8a-89d1-168c485b5d4e_453x414.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>I do not like oAuth. I do not want to understand oAuth. I do not want to create an app. I just want a token for a command-line tool which I will personally use for my own purposes.  Not everything is an app. Not everyone is an app developer.</p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Linux sysadmin log]]></title><description><![CDATA[I use computers to do things and make things.]]></description><link>https://readwithai.substack.com/p/linux-sysadmin-log</link><guid isPermaLink="false">https://readwithai.substack.com/p/linux-sysadmin-log</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Tue, 06 May 2025 00:41:59 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/3217f790-3dac-42c6-93a0-ca73a1273410_1300x708.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I use computers to do things and make things. Sometimes this requires me to <strong><a href="https://readwithai.substack.com/p/my-productivity-tools">create my own tools</a> </strong>but I prefer to <strong><a href="https://readwithai.substack.com/p/tools-i-use">use other people&#8217;s tool</a></strong>. This is a log of things I use on my computer.</p><p><strong>2025-04-30 </strong>Played with <a href="https://superuser.com/questions/1894886/how-do-i-export-an-image-to-clipboard-from-gimp">getting PythonFu to work with GIMP</a> to copy the image to the clipboard. Updated to Gimp 3.0 AppImage for this.</p><p><strong>2025-05-02</strong> <a href="https://github.com/egovelox/mozeidon">mozeidon</a> CLI, written in Go, to get information out of Chrome from the command-line. It can list history and bookmarks and control tabs.</p><p><strong>2025-05-03 </strong><a href="https://github.com/talwrii/yt-cli-rmt-guide">brave-history</a> to dump out and search history. Pretty useful for finding links.</p><p><strong>2025-05-05 </strong>Get CLI control of youtube working with playbackctl - largely just works. <a href="https://github.com/talwrii/yt-cli-rmt-guide">Notes</a>.</p><p><strong>2025-05-05 </strong>Added a bunch of emoji&#8217;s to my commandline to feel cool. Using kitty because it supports emoji&#8217;s: &#128395;&#65039; 25-05-06T2:09:52&#128467;&#65039;  0&#10067;&#65039;0:01&#8987;&#65039;user@thunkpad&#128187;&#65039;/home/user/all/books&#128194;&#9889;&#65039;</p><p><strong>2025-05-05 </strong>Got youtube-dl set up for a little clipping. You can downloads clips you make in youtube. Needed to work out how to get formats working found a <a href="https://github.com/talwrii/awesome-yt-dlp/">decent starting tutorial and various links</a>. </p><p><strong>2025-05-05 </strong>Shortcut to cycle recently used tabs in brave called <a href="https://chromewebstore.google.com/detail/clut-cycle-last-used-tabs/cobieddmkhhnbeldhncnfcgcaccmehgn">CLUT</a> . Found brave://extensions/shortcuts - which allows you to globally change shortcuts.</p><p><strong>2025-05-20 </strong>Wrote a<a href="https://github.com/talwrii/highlight-cli"> little script to highlight text </a>in a stream because I didn&#8217;t like that you had to explicitly match everything with grep.</p><p><strong>2025-05-20 </strong>Learned too much about how escape codes for colors work. There are at least three standards: 16 customizable colors, 256 hard-coded colors; or 16 million colors.</p><p></p><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[My tools for code hosting statistics]]></title><description><![CDATA[@readwithai - X - blog - machine-aided reading]]></description><link>https://readwithai.substack.com/p/my-tools-for-statistics-about-code</link><guid isPermaLink="false">https://readwithai.substack.com/p/my-tools-for-statistics-about-code</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Sat, 26 Apr 2025 23:35:28 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b03cbcd9-0858-410a-ac6f-3eb0656b5241_128x192.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>@readwithai</strong> - <strong><a href="https://x.com/readwithai">X</a></strong> - <strong><a href="https://readwithai.substack.com">blog</a></strong> - <strong><a href="https://www.reddit.com/r/machineAidedReading/">machine-aided reading</a></strong></p><p>I create a lot of <strong><a href="https://readwithai.substack.com/p/my-productivity-tools">command-line tools</a></strong> as <strong><a href="https://readwithai.substack.com">part of my work</a> </strong>some of them are for collecting statistics about the code I release and displaying them in a dashboard.  I list tools for collecting statistics related to code here. I wrote these because they did not exist before I made them.</p><ul><li><p><strong><a href="https://github.com/talwrii/gh-views">gh-views</a></strong> fetches information about the number of pages views and clones for a github repository retaining history information (github only provides a 14 day window.)</p></li><li><p><strong><a href="https://github.com/talwrii/gh-star-timeline">gh-star-timeline</a> </strong>fetches and maintains a historic timeline of stars on a github repo or all your repositories. I like to use it to see how many new stars I got each day, or where the stars can from.</p></li><li><p><strong><a href="https://github.com/talwrii/obsidian-plugin-stats">obsidian-plugin-stats</a> </strong>fetches and retains a timeline about the number of downloads for an <a href="https://readwithai.substack.com/p/what-exactly-is-obsidian">Obsidian</a> plugin.</p></li><li><p><strong><a href="https://github.com/talwrii/pypi-stats-timeline">pypi-stats-timeline</a></strong> fetches, maintains and queries a timeseries of download statistics from the python package index <a href="https://pypi.org/project/pip/">PyPI</a> used by pip.</p><p></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://readwithai.substack.com" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hGfm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01805c06-a905-4b57-bd8f-748e2ddfb2df_128x192.png 424w, https://substackcdn.com/image/fetch/$s_!hGfm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01805c06-a905-4b57-bd8f-748e2ddfb2df_128x192.png 848w, https://substackcdn.com/image/fetch/$s_!hGfm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01805c06-a905-4b57-bd8f-748e2ddfb2df_128x192.png 1272w, https://substackcdn.com/image/fetch/$s_!hGfm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01805c06-a905-4b57-bd8f-748e2ddfb2df_128x192.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hGfm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01805c06-a905-4b57-bd8f-748e2ddfb2df_128x192.png" width="128" height="192" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/01805c06-a905-4b57-bd8f-748e2ddfb2df_128x192.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:192,&quot;width&quot;:128,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:54288,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://readwithai.substack.com&quot;,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://readwithai.substack.com/i/162226953?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01805c06-a905-4b57-bd8f-748e2ddfb2df_128x192.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hGfm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01805c06-a905-4b57-bd8f-748e2ddfb2df_128x192.png 424w, https://substackcdn.com/image/fetch/$s_!hGfm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01805c06-a905-4b57-bd8f-748e2ddfb2df_128x192.png 848w, https://substackcdn.com/image/fetch/$s_!hGfm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01805c06-a905-4b57-bd8f-748e2ddfb2df_128x192.png 1272w, https://substackcdn.com/image/fetch/$s_!hGfm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01805c06-a905-4b57-bd8f-748e2ddfb2df_128x192.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[A practical primer on the kitty-verse]]></title><description><![CDATA[Coming soon!]]></description><link>https://readwithai.substack.com/p/a-practical-primer-on-the-kitty-verse</link><guid isPermaLink="false">https://readwithai.substack.com/p/a-practical-primer-on-the-kitty-verse</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Wed, 23 Apr 2025 03:42:54 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/34985b59-73dd-422e-ac11-65db0b7e68d5_370x555.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Coming soon!</p>]]></content:encoded></item><item><title><![CDATA[Tools I use]]></title><description><![CDATA[I am a programmer so I make my own tools.]]></description><link>https://readwithai.substack.com/p/tools-i-use</link><guid isPermaLink="false">https://readwithai.substack.com/p/tools-i-use</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Tue, 22 Apr 2025 23:57:27 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/efce7e84-e975-4493-b3cc-c6c76deaea6f_466x463.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>@readwithai</strong> - <strong><a href="https://x.com/readwithai">X</a> </strong>-<strong> <a href="https://readwithai.substack.com/">blog</a> </strong>- <strong><a href="https://www.reddit.com/r/machineAidedReading/">machine-aided reading</a></strong></p><p>I am a programmer so I <strong><a href="https://readwithai.substack.com/p/my-productivity-tools">make my own tools</a></strong>. Sometimes these are mean to to change the world in some way. Some they are tools I mean for <strong><a href="https://readwithai.substack.com/p/my-productivity-tools">getting things done</a></strong> while attempting to change the world. Sometimes a mixture of the two.  But where possible, I try to use existing tools for the task I need to get done. Here are some of the tools I use.</p><p></p><p>At the core of everything I use a keyboard. This lets me write down what I think and try to convince other people to do things. I use my own keymapping (<strong><a href="https://readwithai.substack.com/p/symbolboard-an-ergonomic-keyboard">symbolboard</a></strong>) which puts all the modifier controls (control, alt, super) on the number keys so that they are easy to press at the same time. It&#8217;s designed to let me swap to any keyboard without having to relearn anything. I&#8217;ve implemented this one a view different keybinding systems. At the moment, I&#8217;m using <strong><a href="https://github.com/rvaiya/keyd">keyd</a></strong> which allows you to easily defining keymaps at the kernel - in the past I have used xmodmap and xkb but I eventually had problems with both due to how certain apps interacted with them.</p><p>I use <strong><a href="https://x.comy/readwithai">X</a></strong> and <strong><a href="https://readwithai.substack.com/">substack</a> </strong>where I try to convince people that what I am doing is interesting, that what I think is true, and that they can change what they are doing and generally interact with the world.  I need a browser to do this, so I use <strong><a href="https://brave.com/features/">Brave</a> . </strong>I used firefox for a long time, but they had some bugs related to image pasting and also had some bad policies related to data so I switched to brave. I&#8217;ve used Wikipedia for this as well - but there you only get to tell people what is true by making an encyclopedia more correct - you do get to tell a lot of people though. </p><p>I am programmer so when something annoys me or I do it a lot I make tools to help me. For this I use the linux command-line quite a bit. I use <strong><a href="https://wiki.archlinux.org/title/Zsh">zsh</a> </strong>it&#8217;s slightly nicer than bash but probably isn&#8217;t too different. I just use it. I&#8217;m pretty lazy so I use my own snippet system <strong><a href="https://github.com/facetframer/zshnip">zshnip</a> </strong>that lets me define snippets as I go. I tend to insert a lot of links when I&#8217;m writing things so I use <strong><a href="https://github.com/milki/xclip">xclip</a> </strong>to copy things to and from the clipboard for linking. I made a tool called <strong><a href="https://github.com/milki/xclip">brave-bookmarks</a> </strong>to quickly copy links to my bookmarks and also have some tools to interact with my substack pages. I use <strong><a href="https://github.com/junegunn/fzf">fzf</a> </strong>to select things because it removes the needs to type things. I use <strong><a href="https://ffmpeg.org/documentation.html">FFmpeg</a></strong> together with xclip to quickly take screenshots which I can paste into things. I occasionally use <strong><a href="https://github.com/trapd00r/vidir">vidir</a> </strong>when I want to quickly change a files name.</p><p>When note trying to get people&#8217;s attention I do make some things! I use <strong><a href="http://gnuemacs.org/">Emacs</a></strong> for coding. I use <strong><a href="https://en.wikipedia.org/wiki/Vim_(text_editor)">vim</a></strong> bindings via evil-mode. For thinking, learning and planning I use <strong><a href="https://obsidian.md/">Obsidian</a></strong>. In Obsidian I make lots of use of <strong><a href="https://readwithai.substack.com/p/slowly-turning-your-automated-maps">maps of content</a></strong> based on <strong><a href="https://obsidian.rocks/dataview-in-obsidian-a-beginners-guide/">dataview</a></strong>.  I have some plugins to speed up things that I find annoying. I have my own plugin called <strong><a href="https://github.com/talwrii/plugin-repl">Plugin REPL</a></strong> which lets me automate commonly used things. I use <strong><a href="https://git-scm.com/downloads">git</a></strong> a lot. I also use the github command line tool <strong><a href="https://github.com/cli/cli">gh</a> </strong>a bit because I&#8217;m often creating new repositories for tools I share.</p><p>I often need to share data between the different tools I use. I use <strong><a href="https://kde.org/">KDE</a></strong> with its windows tiling to quickly switch between applications and let me see two applications at the same time and I have <strong><a href="https://readwithai.substack.com/p/shortcuts-to-jump-between-applications">keybindings to switch between apps</a></strong> for this I mostly use <strong><a href="https://www.freedesktop.org/wiki/Software/wmctrl/">wmctrl</a></strong>.</p><p></p><p></p><p></p><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Documentation I have written]]></title><description><![CDATA[@readwithai - X - blog - machine-aided reading]]></description><link>https://readwithai.substack.com/p/my-command-line-documentation</link><guid isPermaLink="false">https://readwithai.substack.com/p/my-command-line-documentation</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Mon, 07 Apr 2025 17:11:52 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/1e432eaa-9b61-4479-a545-86247c22707e_458x433.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>@readwithai</strong> - <strong><a href="https://x.com/readwithai">X</a></strong> - <strong><a href="https://readwithai.substack.com">blog</a></strong> - <strong><a href="https://www.reddit.com/r/machineAidedReading/">machine-aided reading</a></strong></p><div><hr></div><p>I am command-line power user, I even wrote <strong><a href="https://github.com/facetframer/zshnip">my own snippet framework</a></strong>. Occasionally I write documentation for the tools I use.</p><p><strong><a href="https://github.com/talwrii/ffmpeg-cookbook">A beginner&#8217;s FFmpeg cookbook</a></strong><a href="https://github.com/talwrii/ffmpeg-cookbook"> </a>an introduction to the command-line video editing tool, FFmpeg&#8217;s, core functionality through examples that you can run, followed by more specialised tasks that link back to the core recipes.</p><p><strong><a href="https://readwithai.substack.com/p/obsidian-plugin-repl-cookbook">Obsidian (and Plugin REPL) cookbook</a> </strong>a cookbook of various recipes for automating and otherwise making your Obsidian set up easier to use.</p><p><strong><a href="https://readwithai.substack.com/p/a-primer-on-linux-desktop-files">A practical primer on Linux desktop files</a></strong> Desktop files are the shell scripts of Desktop Environments like Gnome Shell, Unity or KDE. </p><p><strong><a href="https://readwithai.substack.com/p/creating-kde-keyboard-shortcuts-from">Create keyboard shortcuts for KDE from the command-line</a></strong> It can be a pain to always use KDE&#8217;s settings dialog to create keyboard shortcuts - and I create a lot of keyboard shortcuts. This describes how to create shortcuts in KDE if you want to keep some of the convenience of prettiness of KDE rather than switch to the command line. </p><h2>Lists </h2><p><strong><a href="https://github.com/talwrii/awesome-yt-dlp/blob/main/README.md">awesome yt-dlp</a> </strong>is an <a href="https://github.com/sindresorhus/awesome">awesome list</a> page for the youtube video downloader <a href="https://github.com/yt-dlp/yt-dlp">yt-dlp</a>.</p><h2>Other</h2><p>I have also written a number of blog posts on technical topics which can be found in the <strong><a href="https://readwithai.substack.com/s/technical-miscellany">Technical Miscellany</a></strong> section of this blog. I&#8217;ve written a number of <a href="https://readwithai.substack.com/p/my-productivity-tools">command-line tools</a>.</p><p></p><p></p><p><strong>@readwithai</strong></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hGxX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23c5ea72-8462-4037-a5dc-9e6a82b25992_176x178.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hGxX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23c5ea72-8462-4037-a5dc-9e6a82b25992_176x178.png 424w, https://substackcdn.com/image/fetch/$s_!hGxX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23c5ea72-8462-4037-a5dc-9e6a82b25992_176x178.png 848w, https://substackcdn.com/image/fetch/$s_!hGxX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23c5ea72-8462-4037-a5dc-9e6a82b25992_176x178.png 1272w, https://substackcdn.com/image/fetch/$s_!hGxX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23c5ea72-8462-4037-a5dc-9e6a82b25992_176x178.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hGxX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23c5ea72-8462-4037-a5dc-9e6a82b25992_176x178.png" width="176" height="178" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/23c5ea72-8462-4037-a5dc-9e6a82b25992_176x178.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:178,&quot;width&quot;:176,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:68292,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://readwithai.substack.com/i/160797456?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23c5ea72-8462-4037-a5dc-9e6a82b25992_176x178.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hGxX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23c5ea72-8462-4037-a5dc-9e6a82b25992_176x178.png 424w, https://substackcdn.com/image/fetch/$s_!hGxX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23c5ea72-8462-4037-a5dc-9e6a82b25992_176x178.png 848w, https://substackcdn.com/image/fetch/$s_!hGxX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23c5ea72-8462-4037-a5dc-9e6a82b25992_176x178.png 1272w, https://substackcdn.com/image/fetch/$s_!hGxX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23c5ea72-8462-4037-a5dc-9e6a82b25992_176x178.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><strong> </strong></p>]]></content:encoded></item><item><title><![CDATA[Creating KDE keyboard shortcuts from the command-line]]></title><description><![CDATA[@readwithai - X - blog - FFmpeg through examples]]></description><link>https://readwithai.substack.com/p/creating-kde-keyboard-shortcuts-from</link><guid isPermaLink="false">https://readwithai.substack.com/p/creating-kde-keyboard-shortcuts-from</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Sun, 06 Apr 2025 16:28:39 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/729d18cb-f395-474b-925b-07a28c149e5d_778x526.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>@readwithai - <a href="https://x.com/readwithai">X</a> - <a href="https://substack.readwithai.com/">blog</a> - <a href="https://github.com/talwrii/ffmpeg-cookbook">Learn FFmpeg through examples</a></p><p>I used to be a user of tiling window managers. They are good and all, easy-to-configure, fit within the UNIX philosopher. But they can also be a pain, you end up having to use a keyboard for stuff. Desktop environments at their best make things easier, especially when using a mouse, and using a mouse and not having to &#8220;control&#8221; everything all the time can be relaxing. It&#8217;s odd - I will literally see myself going through short periods where everything has to be done efficiently with the keyboard and then a while later I will happily click away at stuff. It probably varies depending on what I&#8217;m doing. </p><p>For this reason, as well as a feeling that I don&#8217;t want to spend too much time updating my desktop I&#8217;ve opted for to use KDE. KDE is nice and easy to use but its got some tiling-window-manager-lite features with keybindings and the ability to define your own keybindings to do most of the things you want.</p><p>And yet.</p><p>The more I use something the more the day-to-day papercuts stuff to build up - and using all these GUI configuration tools with the continuous click-click-click can get annoying. Sure it&#8217;s easier first time around - well apart from the annoyance of having to find things. But it quickly gets annoying. I don&#8217;t quite know why I find clicking through dialogs so annoying at times - while editing files is easier. I think it is something related to it being hard to find things - whereas in text files you have search and you can&#8217;t &#8220;save your place&#8221; in the same way that you can be programming or editing text files.</p><p>And so today I have learned how to set up keyboard shortcuts from the command-line with KDE and I will share this with you.</p><h1>How shortcuts work in KDE</h1><p>KDE has a file called <strong>~/.config/kglobalshortcutsrc </strong>which contains all your shortcuts. Each shortcuts runs a <a href="https://readwithai.substack.com/p/a-primer-on-linux-desktop-files">desktop file</a> these can either live in <strong>/usr/share/applications </strong>or more likely <strong>~/.local/share/applications. </strong>The KDE shortcut setting tool will create these desktop files for you when you create command shortcut as well an entry in <strong>kglobalshortcutsrc. </strong></p><p>Here is an example .desktop file that KDE created in <strong>~/.local/share/applications </strong>called <strong>focus-obsidian.desktop</strong> which I use to <a href="https://github.com/talwrii/json-wmctrl">raise and cycle through windows</a> for the <a href="https://readwithai.substack.com/p/what-exactly-is-obsidian">note-taking app, Obsidian</a>:</p><pre><code>[Desktop Entry]
Exec=/home/alex/bins/windows/focus-obsidian
Name=/home/alex/bins/windows/focus-obsidian
NoDisplay=true
StartupNotify=false
Type=Application
X-KDE-GlobalAccel-CommandShortcut=true
</code></pre><p><br>Note the <strong>NoDisplay=true </strong>which prevents the normal launcher from showing this entry. The <strong>X-KDE-GlobalAccel-CommandShortcut=true </strong>which says that this entry is for a command. There is then an entry in <strong>~/.config/kglobalshortcutsrc </strong>which looks like so:</p><pre><code>[focus-obsidian.desktop]
_k_friendly_name=focus-obsidian
_launch=Meta+Shift+N,none,/home/alex/bins/windows/focus-obsidian
</code></pre><p>Notice that the name of the entry matches the name of the file. </p><p>The file gets read by a process called <strong>kglobalaccel5</strong></p><h2>Creating shortcuts by hand</h2><p>From this, to create a shortcut by hand we need to:</p><ol><li><p>Create a desktop file</p></li><li><p>Update kglobalshortcutsrc</p></li><li><p>Restart the shortcut daemon</p></li></ol><p>To create the desktop file I normally copy and adapt an existing file. If you remove the  <strong>X-KDE-GlobalAccel-CommandShortcut=true </strong>the shortcut still works but it is shown as program rather than a command in the shortcut settings.</p><p>Let&#8217;s do this now. We&#8217;ll create a shortcut that says &#8220;hello world&#8221; with zenity - you may have to install zenity first.</p><p>First I add the file <strong>hello-world.desktop</strong> to the directory  <strong>/home/alex/.local/share/applications </strong></p><pre><code>[Desktop Entry]
Exec=zenity --info --text "hello world"
Name=Hello World
NoDisplay=true
StartupNotify=false
Type=Application
</code></pre><p><br>Here I have removed the <strong>X-KDE-GlobalAccel-CommandShortcut </strong>option. This means, I believe, that the item is shown as a &#8220;program&#8221; in the shortcuts preferences dialog and KDE does not attempted to update the Name or Exec options. </p><p>Then I edit <strong>~/.config/kglobalshortcutsrc </strong>to add an entry for this desktop file.</p><pre><code>[hello-world.desktop]
_k_friendly_name=Say hello world
_launch=Meta+Ctrl+H,none,This command will say hello
</code></pre><p>Then I restart <strong>kglobalaccel5 </strong>. Unfortunately it seems like this kde daemon does not restart when you kill it, show must be restarted by hand. </p><p>I use the following command to restart kglobalaccel: <strong>killall kglobalaccel5; sleep 1; kglobalaccel5&amp;; disown</strong></p><p>I can then use <strong>Super+Ctrl+H </strong>and see a hello world window pop up. (Note that KDE calls the windows/super key Meta - which is a little confusing since Emacs calls the alt key meta).</p><p>You will notice if you open ~/.config/kglobalshortcutsrc that KDE tries to clean up the file on restart. It will reorder entries so that they are in alphabetically order. And add keys that you miss. If you open the settings for shortcuts you will see an entry under programs. The &#8220;friendly name&#8221; is added to the menu<br></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1jTB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed82356-e65b-4102-b4c5-30cbdf8a31c9_666x474.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1jTB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed82356-e65b-4102-b4c5-30cbdf8a31c9_666x474.png 424w, https://substackcdn.com/image/fetch/$s_!1jTB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed82356-e65b-4102-b4c5-30cbdf8a31c9_666x474.png 848w, https://substackcdn.com/image/fetch/$s_!1jTB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed82356-e65b-4102-b4c5-30cbdf8a31c9_666x474.png 1272w, https://substackcdn.com/image/fetch/$s_!1jTB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed82356-e65b-4102-b4c5-30cbdf8a31c9_666x474.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1jTB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed82356-e65b-4102-b4c5-30cbdf8a31c9_666x474.png" width="666" height="474" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3ed82356-e65b-4102-b4c5-30cbdf8a31c9_666x474.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:474,&quot;width&quot;:666,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:37436,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://readwithai.substack.com/i/160682983?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed82356-e65b-4102-b4c5-30cbdf8a31c9_666x474.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1jTB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed82356-e65b-4102-b4c5-30cbdf8a31c9_666x474.png 424w, https://substackcdn.com/image/fetch/$s_!1jTB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed82356-e65b-4102-b4c5-30cbdf8a31c9_666x474.png 848w, https://substackcdn.com/image/fetch/$s_!1jTB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed82356-e65b-4102-b4c5-30cbdf8a31c9_666x474.png 1272w, https://substackcdn.com/image/fetch/$s_!1jTB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed82356-e65b-4102-b4c5-30cbdf8a31c9_666x474.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i62d!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a4f46a4-ede0-4d42-b94f-9c069e08b63b_868x317.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i62d!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a4f46a4-ede0-4d42-b94f-9c069e08b63b_868x317.png 424w, https://substackcdn.com/image/fetch/$s_!i62d!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a4f46a4-ede0-4d42-b94f-9c069e08b63b_868x317.png 848w, https://substackcdn.com/image/fetch/$s_!i62d!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a4f46a4-ede0-4d42-b94f-9c069e08b63b_868x317.png 1272w, https://substackcdn.com/image/fetch/$s_!i62d!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a4f46a4-ede0-4d42-b94f-9c069e08b63b_868x317.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i62d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a4f46a4-ede0-4d42-b94f-9c069e08b63b_868x317.png" width="868" height="317" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1a4f46a4-ede0-4d42-b94f-9c069e08b63b_868x317.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:317,&quot;width&quot;:868,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19713,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://readwithai.substack.com/i/160682983?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a4f46a4-ede0-4d42-b94f-9c069e08b63b_868x317.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!i62d!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a4f46a4-ede0-4d42-b94f-9c069e08b63b_868x317.png 424w, https://substackcdn.com/image/fetch/$s_!i62d!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a4f46a4-ede0-4d42-b94f-9c069e08b63b_868x317.png 848w, https://substackcdn.com/image/fetch/$s_!i62d!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a4f46a4-ede0-4d42-b94f-9c069e08b63b_868x317.png 1272w, https://substackcdn.com/image/fetch/$s_!i62d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a4f46a4-ede0-4d42-b94f-9c069e08b63b_868x317.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The third entry of the <strong>_launch </strong>item in <strong>kglobalshortcutsrc </strong>is displayed within the details of the shortcut. This key can naturally be longer and provide more details.</p><h1><strong>Some closing thoughts</strong></h1><p>This scratches my itch of avoiding clicking too when creating keyboard shortcuts. It&#8217;s also a little-bit neater. One downside is that the KDE shortcut settings dialog would have detected collisions between shortcuts. So I need to to do this by hand.</p><p>If this is the sort of stuff you find interesting your could: </p><ol><li><p>Check out my beginner&#8217;s guide to <a href="https://github.com/talwrii/ffmpeg-cookbook">FFmpeg through examples that you can run</a>. It&#8217;s got 50-or-so stars on Github in a couple of weeks, so people seem to like it.</p></li><li><p><a href="https://x.com/readwithai">Follow me on X</a> where I post about this sort of stuff and other things.</p></li></ol><p>If you like the cut of my jib you might like to <a href="https://readwithai.substack.com/">check out my blog</a> where I am working on tools for research, reading and agency with the Obsidian note taking tool.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://readwithai.substack.com/p/what-is-reading-broadly-defined" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rgh7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0b00a7-e68b-4279-9a43-a0e880f94ed6_176x178.png 424w, https://substackcdn.com/image/fetch/$s_!rgh7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0b00a7-e68b-4279-9a43-a0e880f94ed6_176x178.png 848w, https://substackcdn.com/image/fetch/$s_!rgh7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0b00a7-e68b-4279-9a43-a0e880f94ed6_176x178.png 1272w, https://substackcdn.com/image/fetch/$s_!rgh7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0b00a7-e68b-4279-9a43-a0e880f94ed6_176x178.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rgh7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0b00a7-e68b-4279-9a43-a0e880f94ed6_176x178.png" width="176" height="178" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6b0b00a7-e68b-4279-9a43-a0e880f94ed6_176x178.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:178,&quot;width&quot;:176,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:68292,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://readwithai.substack.com/p/what-is-reading-broadly-defined&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://readwithai.substack.com/i/160682983?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0b00a7-e68b-4279-9a43-a0e880f94ed6_176x178.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rgh7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0b00a7-e68b-4279-9a43-a0e880f94ed6_176x178.png 424w, https://substackcdn.com/image/fetch/$s_!rgh7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0b00a7-e68b-4279-9a43-a0e880f94ed6_176x178.png 848w, https://substackcdn.com/image/fetch/$s_!rgh7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0b00a7-e68b-4279-9a43-a0e880f94ed6_176x178.png 1272w, https://substackcdn.com/image/fetch/$s_!rgh7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0b00a7-e68b-4279-9a43-a0e880f94ed6_176x178.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[A primer on Linux desktop files]]></title><description><![CDATA[@readwithai - X - blog - computer-assisted reading for more agency]]></description><link>https://readwithai.substack.com/p/a-primer-on-linux-desktop-files</link><guid isPermaLink="false">https://readwithai.substack.com/p/a-primer-on-linux-desktop-files</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Fri, 04 Apr 2025 19:33:05 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/0b7e2b78-0f81-4d2e-a0b0-a33d166bed3b_811x555.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>@readwithai</strong> - <a href="https://x.com/readwithai">X</a> - <a href="https://readwithai.substack.com/">blog</a> -  computer-assisted reading for more agency</p><p>This is a quick primer on desktop files for those with a command-line mentality - who are used to running commands from the command-line, adding things to their path, and creating shell scripts.  Desktop files are how a number of GUI desktop environments like <a href="https://kde.org/plasma-desktop/">KDE plasma</a>, <a href="https://wiki.gnome.org/Projects/GnomeShell">Gnome Shell</a> and <a href="https://ubuntuunity.org/">Ubuntu Unity</a> specify things that are run.</p><p>If you want a technical reference you might like to <a href="https://wiki.archlinux.org/title/Desktop_entries">start with the arch wiki page on the topic</a> . If you want a practical quickstart carry on reading. </p><h2><em>What is is a desktop file and why should I care?</em></h2><p>You don&#8217;t need to use desktop files - you could run everything you want from the terminal, but most people at least run their terminals from a GUI desktop environment, probably use a separate browser and a couple of applications, and a lot of desktop environments work with <a href="https://specifications.freedesktop.org/desktop-entry-spec/latest/">the common standard of .desktop files</a>. In KDE desktop files are used for keyboard shortcuts, items in the launcher and applications that automatically start when you start the desktop environment. If you are going to use a desktop environment you might want to be able to make use of features that use desktop files. But if you are a command-line user your probably want to be able to edit and create these files yourself.</p><p>You <em>can</em> to some degree escape desktop files by using certain tiling or simpler window managers like <a href="https://i3wm.org/">i3</a> or <a href="https://awesomewm.org/">awesome</a>. </p><blockquote><p><em>I am <strong>@readwithai</strong>. I make tools for <strong>reading</strong> and research using the <strong>note taking </strong>tool <strong>Obsidian</strong>. Interesting? Links at the end of the article</em></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7fhg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0ffdee6-50b6-4da6-87c0-98af425f02d7_117x118.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7fhg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0ffdee6-50b6-4da6-87c0-98af425f02d7_117x118.png 424w, https://substackcdn.com/image/fetch/$s_!7fhg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0ffdee6-50b6-4da6-87c0-98af425f02d7_117x118.png 848w, https://substackcdn.com/image/fetch/$s_!7fhg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0ffdee6-50b6-4da6-87c0-98af425f02d7_117x118.png 1272w, https://substackcdn.com/image/fetch/$s_!7fhg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0ffdee6-50b6-4da6-87c0-98af425f02d7_117x118.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7fhg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0ffdee6-50b6-4da6-87c0-98af425f02d7_117x118.png" width="117" height="118" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a0ffdee6-50b6-4da6-87c0-98af425f02d7_117x118.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:118,&quot;width&quot;:117,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:33065,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://readwithai.substack.com/i/160594066?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0ffdee6-50b6-4da6-87c0-98af425f02d7_117x118.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7fhg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0ffdee6-50b6-4da6-87c0-98af425f02d7_117x118.png 424w, https://substackcdn.com/image/fetch/$s_!7fhg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0ffdee6-50b6-4da6-87c0-98af425f02d7_117x118.png 848w, https://substackcdn.com/image/fetch/$s_!7fhg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0ffdee6-50b6-4da6-87c0-98af425f02d7_117x118.png 1272w, https://substackcdn.com/image/fetch/$s_!7fhg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0ffdee6-50b6-4da6-87c0-98af425f02d7_117x118.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div></blockquote><h2><em>What are desktop files used for and why do they exist?</em></h2><p>A desktop file specifies something that can be run. They are similar to an executable that you place on your path - but they provide additional information that desktop environments demand. They make things a bit more complicated than exuectable on the command-line in exchange for certain conveniences like icons, documentation, and localization - amongst other things.</p><p>You can think of desktop files as like a shell script for GUI desktops  A few systems that use .desktop files are: the keyboard shortcut systems, the mime type/ file type system and program launcherps.</p><h2><em>Where do desktop files live and what do they look like? </em></h2><p>Desktop files are an <a href="https://specifications.freedesktop.org/desktop-entry-spec/latest/basic-format.html">INI/TOML-like file</a> that provide a number of key value pairs. Like shell scripts and executables, there is a PATH for desktop entries <a href="https://wiki.archlinux.org/title/Desktop_entries">that consists of</a> <strong>~/.local/share/applications</strong> and <strong>/usr/share/local/applications </strong>and <strong>/usr/share/applications.</strong></p><p>You can explore some open applications files there to see how they work. Desktop files <a href="https://specifications.freedesktop.org/desktop-entry-spec/latest/recognized-keys.html">provide various bits of data including</a> localisation of executables to difference file systems, a tooltip describing an executable called <strong>Comment, </strong>a name calld <strong>Name, </strong>an executable and arguments to run called <strong>Exec </strong>amongst other things.</p><p>Here is a minimal example of a desktop file:</p><pre><code># ~/.local/share/applications/simple.desktop

[Desktop Entry]
Name=A simple desktop entry
Exec=zenity --info ran desktop entry
</code></pre><p>If you create this desktop file then you will be able to run it from your desktop environments&#8217;s launcher menu. You actually don&#8217;t even need the Name entry in the name entry - if omitted the name default to simple.</p><h1><em>Some nice things about desktop entries</em></h1><p>Desktop entries are a bit of a pain compared to an executable on the path. But they provide some benefits - they work across different systems. The mime system is quite nice, because you can ask what things can open a file in a more data-driven way.</p><h2><em>Mime-types and default applications</em></h2><p>If you give the <a href="https://specifications.freedesktop.org/desktop-entry-spec/latest/exec-variables.html">Exec parameter</a> of the desktop file an argument<a href="https://specifications.freedesktop.org/desktop-entry-spec/latest/exec-variables.html"> using </a><strong><a href="https://specifications.freedesktop.org/desktop-entry-spec/latest/exec-variables.html">%f</a> </strong> as well as one or more <a href="https://specifications.freedesktop.org/desktop-entry-spec/latest/recognized-keys.html">MimeTypes</a>, then your desktop will know that your application can handle certain mime types. Your desktop environment can then do useful things like add the application to the &#8220;Open with&#8230;&#8221; menu. </p><p>Here is a simple example that when runs on PDFs or PNGs copies the file to a known location.</p><pre><code># /home/alex/.local/share/applications/copy-to-file.desktop
[Desktop Entry]
Exec=cp %f /tmp/file.pdf
MimeType=application/pdf;application/png
</code></pre><p>Once you create this file, you will notice a <strong>copy-to-file</strong> item in Open With menu on PDF files (though it should be noted that mime types are <a href="https://askubuntu.com/questions/992582/how-do-mimeinfo-cache-files-relate-to-mimeapps-list">handled through other mechanisms as well</a>).</p><p>You can query the default desktop file for a mime type with for example</p><pre><code>xdg-mime query default application/pdf</code></pre><p>In KDE, you can use can use <strong>ktraderclient5 </strong>to find all the apps that support a given mime type: <strong>ktraderclient5 --mimetype application/pdf --servicetype Application</strong>. </p><p>You could then set (careful!) the default mime handler to be this file with:</p><pre><code>xdg-mime default copy-to-file.desktop application/pdf</code></pre><h2><em>Keyboard shortcuts</em></h2><p>KDE seems to create a new desktop file every time you create a custom shortcut. So if you want to create shortcuts by hand understanding desktop files is helpful.</p><h2>Autostart</h2><p>KDE also uses desktop files for the applications that automatically start when KDE starts. These live in <strong>$HOME/autostart </strong>unfortunately gtk-launch, which can be used to run desktop files does not find these files. If you want to test out an autostart file you can use <a href="https://github.com/jceb/dex">dex</a> which runs a desktop file based on a path rather htan searching.</p><h2><em>Running desktop files from the command-line</em></h2><p>When creating desktop files it can be nice to run them in a repeatedly way and the command-line can help here. The best way I found to run desktop files is <strong><a href="https://manpages.ubuntu.com/manpages/focal/man1/gtk-launch.1.html">gtk-launch</a>. </strong>This still works with kde. There are other tools like <a href="https://github.com/jceb/dex">dex</a>, but the nice thing about this is that it searches for the desktop file in different locations like launches rather than requiring a full path. <strong>gtk-launch vim.desktop </strong>launches vim from the command-line.</p><blockquote><p>@readwithai here. If you are interested in reading, note taking or command-line backs follow me on <a href="https://x.com/readwithai">X</a>, my <a href="https://readwithai.substack.com">blog</a>, or <a href="https://readwithai.substack.com/p/what-exactly-is-obsidian">learn about Obsidian</a>.</p></blockquote><h2>Finishing up</h2><p>I hope this was useful. I created this because the world seemed to lack some practical documentation on desktop files (even though the reference documentation is pretty good). </p>]]></content:encoded></item><item><title><![CDATA[My command-line tools]]></title><description><![CDATA[@readwithai - X - blog - machine-aided reading]]></description><link>https://readwithai.substack.com/p/my-productivity-tools</link><guid isPermaLink="false">https://readwithai.substack.com/p/my-productivity-tools</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Thu, 06 Mar 2025 20:38:01 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/7eb489cf-4b1f-4817-9128-8fb753e34e1c_464x354.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>@readwithai - <a href="https://x.com/readwithai">X</a></strong> - <strong><a href="https://readwithai.substack.com/">blog</a></strong> - <strong><a href="https://www.reddit.com/r/machineAidedReading/">machine-aided reading</a></strong></p><p>I like the command line; I like tools written in the &#8220;unix philosophy&#8221;; I like creating general tools that do part of a task; I like using a computer efficiently.</p><p>I prefer to reuse other tools that people have made, here are <strong><a href="https://readwithai.substack.com/p/tools-i-use">some tools that I use</a></strong>. Of course, all of this is in the name of my overarching project of making tools for <strong><a href="https://readwithai.substack.com/">reading, research and agency</a></strong>.</p><p>This is a listing of some tools I have written while serving my whimsy and trying to be efficient at using computers. </p><div><hr></div><h1>Some command-line tools I have made</h1><h3>Web tools</h3><p><strong><a href="https://github.com/talwrii/clixpath?tab=readme-ov-file">clixpath</a> </strong>is a command-line tool to extract elements identified by Xpaths in XML and HTML documents.</p><p><strong><a href="https://github.com/talwrii/curlfire">curlfire</a> </strong>allows you to use firefox cookies from the command line. This can be useful to collection information from pages that use cookies - for example together with clixpath.</p><p><strong><a href="https://github.com/talwrii/profile-fox">profile-fox</a></strong> can be used to open new tabs in a particular firefox profile.</p><p><strong><a href="https://github.com/talwrii/brave-bookmarks">brave-bookmarks</a> </strong>let&#8217;s you access bookmarks from the Brave browser from the command-line.</p><p><strong><a href="https://github.com/talwrii/brave-history">brave-history</a></strong> let&#8217;s you query your history in Brave browser from the command-line</p><p><strong><a href="https://github.com/talwrii/notionshell">notionshell</a></strong> is a command line that can used to update a notion page.</p><h3>Code-hosting statistics</h3><p>I have written various tools to <strong><a href="https://readwithai.substack.com/p/my-tools-for-statistics-about-code">track statistics related to code hosting sites</a></strong>.</p><p><strong><a href="https://github.com/talwrii/gh-views">gh-views</a></strong> fetches information about the number of pages views and clones for a github repository retaining history information (github only provides a 14 day window.)</p><p><strong><a href="https://github.com/talwrii/gh-star-timeline">gh-star-timeline</a> </strong>fetches and maintains a historic timeline of stars on a github repo or all your repositories. I like to use it to see how many new stars I got each day, or where the stars can from.</p><p><strong><a href="https://github.com/talwrii/obsidian-plugin-stats">obsidian-plugin-stats</a> </strong>fetches, maintains and queries a timeseries of the number of downloads for an <a href="https://readwithai.substack.com/p/what-exactly-is-obsidian">Obsidian</a> plugin.</p><p><strong><a href="https://github.com/talwrii/pypi-stats-timeline">pypi-stats-timeline</a></strong> fetches, maintains and queries a timeseries of download statistics from the python package index <a href="https://pypi.org/project/pip/">PyPI</a> used by pip.</p><h3>Shell productivity tools</h3><p><strong><a href="https://gist.github.com/3ed80a2b0d79e3fd5a03df9ff8091594">tmux-result</a></strong> returns the result of the last command by parsing tmux output, if you are using tmux.</p><p><strong><a href="https://github.com/facetframer/zshnip">zshnip</a> </strong>is a zsh snippet framework that I made (and use almost continually) to type less, make fewer mistakes, and be more happy by defining snippets as I go while I use the command line.</p><p><strong><a href="https://github.com/talwrii/kitty-plotnine">kitty-plotnine (k-nine)</a> </strong>is a tool to plot directly in terminals which support the kitty <a href="https://sw.kovidgoyal.net/kitty/graphics-protocol/">Graphics Terminal Protocol</a> such as <a href="https://github.com/kovidgoyal/kitty">kitty</a>, <a href="https://wezterm.org/">wezterm</a>, <a href="https://konsole.kde.org/">konsole</a> (or ghostty - which <a href="https://x.com/readwithai/status/1910398678306865269">you shouldn&#8217;t use</a>)  with shell one-liners.</p><p><strong><a href="https://github.com/talwrii/persist-ssh">persist-ssh</a> </strong>is a wrapper around ssh which uses <a href="https://github.com/crigler/dtach">dtach</a> to persist your ssh connection while retaining your shell scrollback. I use this so that I can run tmux on my machine while sshing into other machines instead of running tmux remotely.</p><h3>Sysadmin tools</h3><p><strong><a href="https://github.com/talwrii/env-subset">env-subset</a> </strong>is a swiss-army knife of running commands with a different set of environments written in response to the limits of the env command-line tool</p><p><strong><a href="https://github.com/talwrii/killable-sudo">killable-sudo</a></strong> runs a process with sudo but in such a way that the process can be killed by the user who spawned it, for example by process managers like <a href="https://circus.readthedocs.io/en/latest/man/circusd/">circus</a> or <a href="https://supervisord.org/">supervisord</a>. This is particularly useful for automated users using sudo as a form of <a href="https://unix.stackexchange.com/questions/631449/how-to-run-specific-command-with-sudo-and-no-password">limited increased privilege</a>.</p><h3>Home automation</h3><p>Home automation allow aspects of your physical life to be made easier, more enjoyable and more productive. I therefore use some home automation technologies - mostly for physical buttons and switching devices on and off.</p><p><strong><a href="https://github.com/talwrii/tuya-tiny-web">tuya-tiny-web</a> </strong>is a tool to control (though not set up) tuya wifi devices locally through a web service. It can be used over unix domain sockets to limit access. For those seeking customized programmatic control, I would recommend buying zigbee devices - but I bought a number of these devices before understand suck nuances.</p><h3>LLM tools</h3><p>I was using LLMs slightly before they were massively trendy.</p><p><strong><a href="https://github.com/talwrii/human-tar">human-tar</a> </strong>is a tool to handle a human-readable format inspired by tar which contains multiple files. llm&#8217;s can handle these files.<strong> </strong></p><h3>GUI automation</h3><p><strong><a href="https://github.com/talwrii/xclip-json">xclip-json</a> </strong>displays all of the targets (types of data) on the X11 clipboard in machine-readable JSON. Tools tend to place multiple things on the clipboard and knowing the complete clipboard can be helpful.</p><p><strong><a href="https://github.com/talwrii/clipboard-many-files">clipboard-many-files</a> </strong>is a command-line tool to place multiple files on the clipboard on X11 for linux.</p><p><strong><a href="https://github.com/talwrii/x-open-point'">x-open-point</a> </strong>will open an application provided on the command line at a particular location and size on the screen.</p><h2>i3wm tools</h2><p>I used to use the i3 tiling window manager and wrote a few tools for this. I am mostly using KDE at the moment.</p><h3>JSON tools</h3><p><strong><a href="https://github.com/talwrii/json-wmctrl">json-wmctrl</a> </strong>is command-line tool to display information about Linux X windows in a JSON format that can be searched with jq with other programs. It is named after the program wmctrl that performs similarly functionality but in a less machine readable way.  It provides an option to cycle windows</p><p><strong><a href="https://github.com/talwrii/json-xwininfo">json-xwininfo</a></strong> gets information about a window in X11 from the command-line.</p><p><strong><a href="https://github.com/talwrii/json-leaves">json-leaves</a> </strong>reads an object in JSON and outputs all of the values within it along with a &#8220;path&#8221; to the value suitable for grepping. This can then used in <a href="https://jqlang.org/">jq</a>, Python or JavaScript. This has lots of flags for varying the output</p><p><strong><a href="https://github.com/talwrii/short_schema">short_schema</a> </strong>is a tool to understand the shape of JSON data without having to read it by creating a easily readable schema of the JSON data with the help of <a href="https://github.com/wolverdude/GenSON">GenSON</a>.</p><div><hr></div><p>If this is the sort of stuff you find interesting. You might like to <strong><a href="https://github.com/talwrii">follow me on github</a></strong>, or <strong><a href="https://x.com/readwithai">on X</a></strong> where I post about such things. You might also like to read the <strong><a href="https://readwithai.substack.com/s/technical-miscellany">technical miscellany</a></strong> section of my blog.</p><p>Also, let me introduce myself - I&#8217;m <strong>readwithai</strong> when not creating productivity tools I&#8217;m interested in agency, research and reading and making tools related to this - sometimes using <strong><a href="https://readwithai.substack.com/p/what-exactly-is-obsidian">Obsidian</a></strong>. If this is interested in you might like to <strong><a href="https://readwithai.substack.com/">read my blog</a></strong>. </p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bidI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e8b4241-604e-448e-adc2-732b9c27f9be_176x178.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bidI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e8b4241-604e-448e-adc2-732b9c27f9be_176x178.png 424w, https://substackcdn.com/image/fetch/$s_!bidI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e8b4241-604e-448e-adc2-732b9c27f9be_176x178.png 848w, https://substackcdn.com/image/fetch/$s_!bidI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e8b4241-604e-448e-adc2-732b9c27f9be_176x178.png 1272w, https://substackcdn.com/image/fetch/$s_!bidI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e8b4241-604e-448e-adc2-732b9c27f9be_176x178.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bidI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e8b4241-604e-448e-adc2-732b9c27f9be_176x178.png" width="176" height="178" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7e8b4241-604e-448e-adc2-732b9c27f9be_176x178.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:178,&quot;width&quot;:176,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:68292,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://readwithai.substack.com/i/158541365?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e8b4241-604e-448e-adc2-732b9c27f9be_176x178.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!bidI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e8b4241-604e-448e-adc2-732b9c27f9be_176x178.png 424w, https://substackcdn.com/image/fetch/$s_!bidI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e8b4241-604e-448e-adc2-732b9c27f9be_176x178.png 848w, https://substackcdn.com/image/fetch/$s_!bidI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e8b4241-604e-448e-adc2-732b9c27f9be_176x178.png 1272w, https://substackcdn.com/image/fetch/$s_!bidI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e8b4241-604e-448e-adc2-732b9c27f9be_176x178.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><br></p><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Shortcuts to jump between applications with linux and X11]]></title><description><![CDATA[One of the best &#8220;productivity&#8221; hacks that I use on my machine is creating keybindings to swap between commonly used apps.]]></description><link>https://readwithai.substack.com/p/shortcuts-to-jump-between-applications</link><guid isPermaLink="false">https://readwithai.substack.com/p/shortcuts-to-jump-between-applications</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Sun, 23 Feb 2025 20:09:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!PXgF!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27d164e2-31fb-4b1b-a296-5ea6264ea8d0_128x128.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One of the best &#8220;productivity&#8221; hacks that I use on my machine is creating keybindings to swap between commonly used apps. On mac I used <a href="https://karabiner-elements.pqrs.org/">Karabiner</a> together with some apple script. On Linux I use various scripts like  <strong>wmctrl  -x -a Firefox </strong>different keybindings for different apps. I tend to use <strong>Super+Shift+Letter</strong> for these bindings and then use <strong>Super+Alt+Letter </strong>for bindings related to &#8220;desktop actions&#8221;.</p><p>I&#8217;m using KDE at the moment in which creating these shortcuts is fairly straightforward. In the past, I have used tiling window managers such as i3 and lua where this is equally straight forward. </p><h2>The keybindings I use</h2><p>I end up have a shortcut for: Firefox, Obsidian, a terminal, and Emacs. For other things I fall back to using <a href="https://github.com/davatorium/rofi">rofi</a> which provides a fuzzy switcher based on window titles</p><h2>Ensuring that the PATH is set correctly</h2><p>One consistent annoyance is ensuring that the path is set correctly when an application is launched by the window manager.</p><p>I have two tricks here. </p><p>First you can use <strong>env -i command </strong>to run a command with an empty environment from the shell - you can then make sure that your command will work regardless of the environment that your window manager provides.</p><p>I also have a wrapper called <strong>withbashrc </strong>which runs bashrc and then runs a command so that everything I add to my bashrc is still on my path when I run something.</p><pre><code>#!/bin/bash

export PATH=$PATH:/usr/bin:~/.extraconfig/bins/pathmerge
export HOME=~

export NO_SSH_AGENT

source ~/.bashrc
"$@"
</code></pre><p>I use this like <strong>withbashrc command</strong></p><h2>Cycling windows</h2><p>wmctrl works well enough, provided you have precisely one window. I tend to do this because it makes switching app windows&#8230; but I&#8217;m experimenting with having a couple of <a href="https://readwithai.substack.com/p/what-exactly-is-obsidian">Obsidian</a> windows open at the moment while working on some <a href="https://readwithai.substack.com/p/what-is-reading-broadly-defined">tools for reading</a>. For this, I have a solution where I order all windows arbitrarily and then cycle to the next unfocused Obsidian if I press the &#8220;raise Obsidian keybinding again&#8221;. </p><p>Because the output of <strong>wmctrl </strong>didn&#8217;t have all the informaion I needed (namely the window class) in its output from <strong>wmctrl -l </strong>I ended up writing my own link wmctrl that output json information and had an option <strong>--next-unfocused </strong>to cycle through windows. I put this in <a href="https://github.com/talwrii/json-wmctrl">pip</a> and on <a href="https://github.com/talwrii/json-wmctrl">github</a> so it is available everywhere.</p><p>I then used the following command:</p><pre><code>json-wmctrl  --class "obsidian" --next-unfocused | jq .id  | xargs wmctrl -i -a</code></pre><p>Which either raised the first obsidian window or cycles through the windows. </p><h2>Open that app if it not running</h2><p>The world is simpler if you have fewer concepts and fewer moving parts, so it can be simpler if you use the same keybinding to both open an app and raise it. I haven&#8217;t gotten around to get this working when also cycling between windows, though this wouldn&#8217;t take too much work. </p><p>If you only have a single window for an app a nice approach is to use the <a href="https://github.com/Soft/run-or-raise">raise-or-run</a> command line-tool, written in Rust. You can install this with: </p><pre><code>cargo install run-or-raise</code></pre><p>(If you search on github, you will find a lot of different run-or-raise programs!).<br><br>I then use the following command to, for example, start or raise the brave browser. I use <a href="https://github.com/talwrii/json-wmctrl">json-wmtrl</a> as above to get the class for this query.</p><pre><code>run-or-raise 'class = "Brave-browser"' brave-browser</code></pre><p></p><h2>Finishing up</h2><p>So I&#8217;ve documented the tricks I use. I have this is useful. If you find this interested you might like to:</p><ol><li><p>Have a look at <a href="https://github.com/talwrii/json-wmctrl">this command-line to list windows in JSON</a> that I made.</p></li><li><p>Look at  <a href="https://readwithai.substack.com/s/technical-miscellany">various other technical hacks on my blog</a></p></li><li><p>Have a look at <a href="https://readwithai.substack.com/p/note-taking-with-obsidian-much-of">my review of how people use Obsidian for note taking</a>. I find maintaining a bit of a &#8220;second brain&#8221; to hold my hands while doing work quite handy and this summarize some use cases.<br></p></li></ol><div><hr></div><p>I am readwithai. I make tools related to reading, agency and productivity often related to Obsidian and write a bit about this. </p><p>If any of this seems interesting you can follow me on <a href="https://x.com/readwithai">X</a> where I often post about cli / linux tricks, or follow me on my <a href="https://readwithai.substack.com/">blog</a> where I intend to post more about work related to Obsidian - part form this selection.</p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Keeping on-path Python scripts in the brave new virtualenv world]]></title><description><![CDATA[I have a confession to make.]]></description><link>https://readwithai.substack.com/p/keeping-on-path-python-scripts-in</link><guid isPermaLink="false">https://readwithai.substack.com/p/keeping-on-path-python-scripts-in</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Sun, 16 Feb 2025 17:43:20 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/2be1bb27-91f1-4cd6-a6fb-65733f2771eb_947x665.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ISRs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F092ab184-506e-48dc-b426-ddc87cc44e7f_947x431.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ISRs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F092ab184-506e-48dc-b426-ddc87cc44e7f_947x431.png 424w, https://substackcdn.com/image/fetch/$s_!ISRs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F092ab184-506e-48dc-b426-ddc87cc44e7f_947x431.png 848w, https://substackcdn.com/image/fetch/$s_!ISRs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F092ab184-506e-48dc-b426-ddc87cc44e7f_947x431.png 1272w, https://substackcdn.com/image/fetch/$s_!ISRs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F092ab184-506e-48dc-b426-ddc87cc44e7f_947x431.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ISRs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F092ab184-506e-48dc-b426-ddc87cc44e7f_947x431.png" width="947" height="431" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/092ab184-506e-48dc-b426-ddc87cc44e7f_947x431.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:431,&quot;width&quot;:947,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:68588,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ISRs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F092ab184-506e-48dc-b426-ddc87cc44e7f_947x431.png 424w, https://substackcdn.com/image/fetch/$s_!ISRs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F092ab184-506e-48dc-b426-ddc87cc44e7f_947x431.png 848w, https://substackcdn.com/image/fetch/$s_!ISRs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F092ab184-506e-48dc-b426-ddc87cc44e7f_947x431.png 1272w, https://substackcdn.com/image/fetch/$s_!ISRs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F092ab184-506e-48dc-b426-ddc87cc44e7f_947x431.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Update: It looks like uv supports <a href="https://akrabat.com/using-uv-as-your-shebang-line/">per script run time environments specified in file</a> which achieves the same thing as this tool and is more standard. I suggest you use this.</strong></p><p>I have a confession to make. I used to use my system Python for a lot of things&#8230; and it wasn&#8217;t really that much of an issue. Sure for <em>real</em> work I would use <a href="https://docs.python.org/3/library/venv.html">virtualenvs</a> but for the day-to-day tools that made my life easier, I would just shove a <a href="https://unix.stackexchange.com/questions/26047/how-to-correctly-add-a-path-to-path">script on my path</a>, use system Python to run it and things worked okay.</p><p>But it seems like these days are over, as Ubuntu has claimed ownership of python3 and told you that<a href="https://www.reddit.com/r/learnpython/comments/1338la7/you_cant_use_pip_on_ubuntu_2304_anymore/"> you can&#8217;t install things with pip</a> . It&#8217;s an understandable move, but it does require me to reform my erstwhile ways&#8230; a bit. </p><p>If I was truly incorrigible, I would just create a virtualenv, link to its python, calling my version something like <strong>my-python </strong>and then use this for all my scripts. Or even activate a virtualenv in my bashrc. But&#8230; I felt like I should at least show a little willing so I&#8217;m using a different approach</p><h1>Using PATH with a few virtualenvs</h1><p>The approach that I decided to go with is still having Python scripts on my path - but have different directories on my path maintain their own Python. For example I have a directory with tools for command line video processing and in this directory I have version of python called <strong>video-python </strong>which I use in this directory.</p><p>I&#8217;ll do a quick tour of how this works. First I create a virtualenv in my directory (importantly, on my path) with this <strong>python3 -m venv env. </strong>Then I create a shim &#8220;python&#8221; called video-python which calls the Python in env/bin/python. I need to use a little bash magic here because symlinks appear to break virtualenvs. Here is the contents of </p><pre><code>#!/bin/bash

here="$(dirname ${BASH_SOURCE[0]})"
$here/env/bin/python "$@"</code></pre><p>I also do the same with pip to create <strong>video-pip.</strong></p><p>Then in Python scripts that use this Python I have the following shbang (which works because video-python is on my path)</p><pre><code>#!/usr/bin/env video-python</code></pre><h1>Turning this into a tool</h1><p>Because I have quite a few directories like this, I wrapped up the logic to create these virtualenvs in a little tool called <a href="https://github.com/talwrii/venv-python">venv-python</a>. </p><p>You can install this with pipx.</p><pre><code>pipx install venv-python</code></pre><p><br>You can then run this in the directory where you want to create a Python like so</p><pre><code>cd bins/video
venv-python video</code></pre><h1>A note of the &#8220;correct&#8221; way of doing this</h1><p>So there is another way of doing this, and it&#8217;s what I do when I make &#8220;big&#8221; tools that either need testing or want to be shared. That to create a Python project with an entry point in setup.py</p><pre><code>    entry_points={
        'console_scripts': ['venv-python=venv_python.main:main']
    },
)
</code></pre><p><br>You can then install this using <a href="https://github.com/pypa/pipx">pipx</a> with <strong>pipx install -f -e . </strong> . This allows you to edit the code underlying your script and works well enough. But you need to edit these entry points and rerun this command every thing you want some new scripts.</p><p>This is all just wiring so there are probably other tricks you can use. </p><h1>Finishing up</h1><p>I hope this is interesting, if it is you might like to follow me on <a href="https://x.com/readwithai">X</a> or my blog <a href="https://readwithai.substack.com">where I talk about reading tools and agency</a> and also other technical stuff I do. I might suggest you could also like:</p><ol><li><p>This post about <a href="https://readwithai.substack.com/p/sending-code-to-a-python-virtualenv">sending code to ipython kernel in a virtualenv from Emacs</a></p></li></ol><ol start="2"><li><p>This command-line tool to use <a href="https://github.com/talwrii/npcli/blob/master/README.md">numpy from the shell</a></p></li><li><p>This library that customizes pdb to be <a href="https://github.com/talwrii/tomspdb">more useable by adding custom commands</a>.</p></li></ol><p></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://readwithai.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://readwithai.substack.com/subscribe?"><span>Subscribe now</span></a></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Navigating Typescript in Emacs with tide-nav.el]]></title><description><![CDATA[A useful function that I borrowed from a colleague&#8217;s emacs.d setup was a short that returned which class or function the cursor is in when programming in Python.]]></description><link>https://readwithai.substack.com/p/navigating-typescript-in-emacs-with</link><guid isPermaLink="false">https://readwithai.substack.com/p/navigating-typescript-in-emacs-with</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Tue, 04 Feb 2025 19:20:07 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f6ca7a5c-70b0-4b12-948b-b6214ed073d6_749x293.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://x.com/readwithai/status/1886877677292019960" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!c_yU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42d1930b-c4c2-4ddf-a46b-06aa527e6af5_629x396.png 424w, https://substackcdn.com/image/fetch/$s_!c_yU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42d1930b-c4c2-4ddf-a46b-06aa527e6af5_629x396.png 848w, https://substackcdn.com/image/fetch/$s_!c_yU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42d1930b-c4c2-4ddf-a46b-06aa527e6af5_629x396.png 1272w, https://substackcdn.com/image/fetch/$s_!c_yU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42d1930b-c4c2-4ddf-a46b-06aa527e6af5_629x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!c_yU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42d1930b-c4c2-4ddf-a46b-06aa527e6af5_629x396.png" width="629" height="396" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/42d1930b-c4c2-4ddf-a46b-06aa527e6af5_629x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:396,&quot;width&quot;:629,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:88935,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://x.com/readwithai/status/1886877677292019960&quot;,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!c_yU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42d1930b-c4c2-4ddf-a46b-06aa527e6af5_629x396.png 424w, https://substackcdn.com/image/fetch/$s_!c_yU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42d1930b-c4c2-4ddf-a46b-06aa527e6af5_629x396.png 848w, https://substackcdn.com/image/fetch/$s_!c_yU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42d1930b-c4c2-4ddf-a46b-06aa527e6af5_629x396.png 1272w, https://substackcdn.com/image/fetch/$s_!c_yU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42d1930b-c4c2-4ddf-a46b-06aa527e6af5_629x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p></p><p>A useful function that I borrowed from a colleague&#8217;s emacs.d setup was a shortcut that returned which class or function the cursor is in when programming in Python. I used this quite a lot. I&#8217;ve found myself exploring quite a lot of TypeScript while developing some PDF reading features for Obsidian, found myself wanting this sort of feature, and so decided to implement.  Hence we now have the <a href="https://github.com/talwrii/tide-nav.el">tide-nav.el</a> a library in Emacs for navigating an querying TypeScript - based upon the <a href="https://github.com/ananthakumaran/tide">tide</a> TypeScript IDE library.</p><p><strong><a href="https://github.com/talwrii/tide-nav.el">tide-nav</a></strong> provides a few different functions. </p><p>The <strong>tide-nav-which-class </strong>and <strong>tide-nav-which-function </strong>show you which class or function you are in. This can be very useful when navigating large files.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!k1HE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8f5ccf5-1e86-45c6-8114-30959263a3e2_960x1008.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!k1HE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8f5ccf5-1e86-45c6-8114-30959263a3e2_960x1008.gif 424w, https://substackcdn.com/image/fetch/$s_!k1HE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8f5ccf5-1e86-45c6-8114-30959263a3e2_960x1008.gif 848w, https://substackcdn.com/image/fetch/$s_!k1HE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8f5ccf5-1e86-45c6-8114-30959263a3e2_960x1008.gif 1272w, https://substackcdn.com/image/fetch/$s_!k1HE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8f5ccf5-1e86-45c6-8114-30959263a3e2_960x1008.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!k1HE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8f5ccf5-1e86-45c6-8114-30959263a3e2_960x1008.gif" width="960" height="1008" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c8f5ccf5-1e86-45c6-8114-30959263a3e2_960x1008.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1008,&quot;width&quot;:960,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1800410,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!k1HE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8f5ccf5-1e86-45c6-8114-30959263a3e2_960x1008.gif 424w, https://substackcdn.com/image/fetch/$s_!k1HE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8f5ccf5-1e86-45c6-8114-30959263a3e2_960x1008.gif 848w, https://substackcdn.com/image/fetch/$s_!k1HE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8f5ccf5-1e86-45c6-8114-30959263a3e2_960x1008.gif 1272w, https://substackcdn.com/image/fetch/$s_!k1HE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8f5ccf5-1e86-45c6-8114-30959263a3e2_960x1008.gif 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!asu9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc956f385-b833-4b87-a2e9-c71e532107da_960x1008.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!asu9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc956f385-b833-4b87-a2e9-c71e532107da_960x1008.gif 424w, https://substackcdn.com/image/fetch/$s_!asu9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc956f385-b833-4b87-a2e9-c71e532107da_960x1008.gif 848w, https://substackcdn.com/image/fetch/$s_!asu9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc956f385-b833-4b87-a2e9-c71e532107da_960x1008.gif 1272w, https://substackcdn.com/image/fetch/$s_!asu9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc956f385-b833-4b87-a2e9-c71e532107da_960x1008.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!asu9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc956f385-b833-4b87-a2e9-c71e532107da_960x1008.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c956f385-b833-4b87-a2e9-c71e532107da_960x1008.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2915076,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!asu9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc956f385-b833-4b87-a2e9-c71e532107da_960x1008.gif 424w, https://substackcdn.com/image/fetch/$s_!asu9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc956f385-b833-4b87-a2e9-c71e532107da_960x1008.gif 848w, https://substackcdn.com/image/fetch/$s_!asu9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc956f385-b833-4b87-a2e9-c71e532107da_960x1008.gif 1272w, https://substackcdn.com/image/fetch/$s_!asu9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc956f385-b833-4b87-a2e9-c71e532107da_960x1008.gif 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p>Then there are some functions for jumping back to the start of a function or class definition: <strong>tide-nav-back-class</strong> and <strong>tide-nav-back-function.</strong></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FKG_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97bf2269-ed89-4cd5-bc6f-e717d51ac711_960x1008.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FKG_!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97bf2269-ed89-4cd5-bc6f-e717d51ac711_960x1008.gif 424w, https://substackcdn.com/image/fetch/$s_!FKG_!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97bf2269-ed89-4cd5-bc6f-e717d51ac711_960x1008.gif 848w, https://substackcdn.com/image/fetch/$s_!FKG_!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97bf2269-ed89-4cd5-bc6f-e717d51ac711_960x1008.gif 1272w, https://substackcdn.com/image/fetch/$s_!FKG_!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97bf2269-ed89-4cd5-bc6f-e717d51ac711_960x1008.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FKG_!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97bf2269-ed89-4cd5-bc6f-e717d51ac711_960x1008.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/97bf2269-ed89-4cd5-bc6f-e717d51ac711_960x1008.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6456967,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FKG_!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97bf2269-ed89-4cd5-bc6f-e717d51ac711_960x1008.gif 424w, https://substackcdn.com/image/fetch/$s_!FKG_!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97bf2269-ed89-4cd5-bc6f-e717d51ac711_960x1008.gif 848w, https://substackcdn.com/image/fetch/$s_!FKG_!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97bf2269-ed89-4cd5-bc6f-e717d51ac711_960x1008.gif 1272w, https://substackcdn.com/image/fetch/$s_!FKG_!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97bf2269-ed89-4cd5-bc6f-e717d51ac711_960x1008.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p><p>I may at some point in the future (probably next time I do a lot of TypeScript code reading) add functions to navigate to the next class or function.</p><h2><strong>Blocks</strong></h2><p>I also provide some functions which act on &#8220;blocks&#8221; (namespaces, classes, functions of methods) in case you don&#8217;t want to worry about the difference between functions: <strong>tide-nav-back-block</strong>, <strong>tide-nav-which-block.</strong></p><h2>A little look under the hood</h2><p>If you are interested in how this works. The function <strong>tide-command:navbar </strong>returns a complete parsed tree of the file and the rest of the functionality does tree type operations of this tree. I have a big of logic to find the &#8220;closest&#8221; tide node to the point.</p><p>There is a little hacky code to handle TypeScript that looks like this:</p><pre><code>const func = () =&gt; {

}</code></pre><p>Irritatingly, tide does not, as far as I can tell, let you distinguish between these lambda expressions and constants. Because of this, I use the number of lines that the tide node covers as a proxy for whether this is a function of just a constant.</p><h2>Final thoughts</h2><p>So that&#8217;s that. It&#8217;s worth noting that other people might use some sort of code browser for this. I&#8217;ve never quite got on with such things - they require too much clicking. <strong>imenu </strong>does allow you to jump to &#8220;nodes&#8221; in the tide tree.</p><p>If you want to use this library check out the <a href="https://github.com/talwrii/tide-nav.el">github page</a>.  </p><div><hr></div><p><em>I am readwithai. I am working on developing tools and writing around for productivity, agency and reading mostly around Obsidian. </em></p><p><em>If you are an emacs user who is interested in Obsidian you might like to have a look at my plugin <a href="https://github.com/talwrii/plugin-repl">Plugin REPL</a> which adds Emacs like features for scripting Obsidian like you can script Emacs. Or you might like to read this page on <a href="https://readwithai.substack.com/p/obsidian-for-emacs-org-mode-users">Obsidian for Org mode users</a>. </em></p><p><em>If this sounds interesting you can follow me on <a href="https://x.com/readwithai">X</a> or on my <a href="https://readwithai.substack.com">blog</a>:</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://readwithai.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://readwithai.substack.com/subscribe?"><span>Subscribe now</span></a></p><p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!77Z9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00756889-718d-4586-a280-1f20f9188e01_1004x730.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!77Z9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00756889-718d-4586-a280-1f20f9188e01_1004x730.gif 424w, https://substackcdn.com/image/fetch/$s_!77Z9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00756889-718d-4586-a280-1f20f9188e01_1004x730.gif 848w, https://substackcdn.com/image/fetch/$s_!77Z9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00756889-718d-4586-a280-1f20f9188e01_1004x730.gif 1272w, https://substackcdn.com/image/fetch/$s_!77Z9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00756889-718d-4586-a280-1f20f9188e01_1004x730.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!77Z9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00756889-718d-4586-a280-1f20f9188e01_1004x730.gif" width="1004" height="730" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/00756889-718d-4586-a280-1f20f9188e01_1004x730.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:730,&quot;width&quot;:1004,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2322718,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!77Z9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00756889-718d-4586-a280-1f20f9188e01_1004x730.gif 424w, https://substackcdn.com/image/fetch/$s_!77Z9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00756889-718d-4586-a280-1f20f9188e01_1004x730.gif 848w, https://substackcdn.com/image/fetch/$s_!77Z9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00756889-718d-4586-a280-1f20f9188e01_1004x730.gif 1272w, https://substackcdn.com/image/fetch/$s_!77Z9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00756889-718d-4586-a280-1f20f9188e01_1004x730.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p>]]></content:encoded></item><item><title><![CDATA[Linux mouse record]]></title><description><![CDATA[Quickly automating clicks on linux]]></description><link>https://readwithai.substack.com/p/linux-mouse-record</link><guid isPermaLink="false">https://readwithai.substack.com/p/linux-mouse-record</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Sat, 25 Jan 2025 11:42:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!PXgF!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27d164e2-31fb-4b1b-a296-5ea6264ea8d0_128x128.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a href="https://x.com/readwithai">@readwithai </a>- <a href="https://x.com/readwithai">X</a> - <a href="https://readwithai.substack.com">blog</a> </p><p>I&#8217;ve made a small command line tool called <a href="https://github.com/talwrii/linux-mouse-record">linux-mouse-record</a>, which records and replays mouse clicks on Linux (and probably most OSs). I found a number of too-complicated-for-me-to-use-now GUI tools for this, and a few command line tools that didn&#8217;t work, some that didn&#8217;t support linux, and a lot without any real documentation. So I guess I&#8217;m adding my tool to the mix. </p><p>It is <a href="https://pypi.org/project/linux-mouse-record/">hosted on PyPI</a> and you install it with <a href="https://pypi.org/project/linux-mouse-record/">pipx</a> </p><pre><code>pipx install linux-mouse-record</code></pre><p>You use it like this:</p><pre><code>
linux-mouse-record &gt; events.jsonl
linux-mouse-replay &lt; events.jsonl
</code></pre><p><br>The main value is really getting screen coordinates. It&#8217;s one of command-line tools for GUI automation.</p><div><hr></div><p><em>I am <strong>@readwithai.</strong> I make tools for reading and research often using <a href="https://readwithai.substack.com/p/what-exactly-is-obsidian">Obsidian</a>. You can follow me on <a href="https://x.com/readwithai">X</a> or <a href="https://readwithai.substack.com">substack</a></em></p><p></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://readwithai.substack.com/" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5ZKb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F864413de-1bda-4787-8a25-46c45afc7e52_128x192.png 424w, https://substackcdn.com/image/fetch/$s_!5ZKb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F864413de-1bda-4787-8a25-46c45afc7e52_128x192.png 848w, https://substackcdn.com/image/fetch/$s_!5ZKb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F864413de-1bda-4787-8a25-46c45afc7e52_128x192.png 1272w, https://substackcdn.com/image/fetch/$s_!5ZKb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F864413de-1bda-4787-8a25-46c45afc7e52_128x192.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5ZKb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F864413de-1bda-4787-8a25-46c45afc7e52_128x192.png" width="128" height="192" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/864413de-1bda-4787-8a25-46c45afc7e52_128x192.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:192,&quot;width&quot;:128,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:54288,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://readwithai.substack.com/&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://readwithai.substack.com/i/155682519?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F864413de-1bda-4787-8a25-46c45afc7e52_128x192.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5ZKb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F864413de-1bda-4787-8a25-46c45afc7e52_128x192.png 424w, https://substackcdn.com/image/fetch/$s_!5ZKb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F864413de-1bda-4787-8a25-46c45afc7e52_128x192.png 848w, https://substackcdn.com/image/fetch/$s_!5ZKb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F864413de-1bda-4787-8a25-46c45afc7e52_128x192.png 1272w, https://substackcdn.com/image/fetch/$s_!5ZKb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F864413de-1bda-4787-8a25-46c45afc7e52_128x192.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>.</em></p>]]></content:encoded></item><item><title><![CDATA[Instantly report users on X using a bookmarklet]]></title><description><![CDATA[By @readwithai - Creating tools for reading and agency with Obsidian. Sharing my work and thoughts along the way.]]></description><link>https://readwithai.substack.com/p/install-report-users-on-x-using-a</link><guid isPermaLink="false">https://readwithai.substack.com/p/install-report-users-on-x-using-a</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Sun, 05 Jan 2025 00:03:36 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!K5TK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90f56fff-737b-4907-91e6-e46b89ffc51e_598x414.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>By <a href="https://x.com/readwithai">@readwithai</a> - <a href="https://github.com/talwrii">Creating</a> tools for <a href="https://x.com/readwithai/status/1850511849655046602">reading</a> and <a href="https://readwithai.substack.com/p/reading-and-agency">agency</a> with <a href="https://readwithai.substack.com/p/obsidian-what-and-why">Obsidian</a>. Sharing my work and thoughts along the way.</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://x.com/readwithai/status/1875699504625893406" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!K5TK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90f56fff-737b-4907-91e6-e46b89ffc51e_598x414.png 424w, https://substackcdn.com/image/fetch/$s_!K5TK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90f56fff-737b-4907-91e6-e46b89ffc51e_598x414.png 848w, https://substackcdn.com/image/fetch/$s_!K5TK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90f56fff-737b-4907-91e6-e46b89ffc51e_598x414.png 1272w, https://substackcdn.com/image/fetch/$s_!K5TK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90f56fff-737b-4907-91e6-e46b89ffc51e_598x414.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!K5TK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90f56fff-737b-4907-91e6-e46b89ffc51e_598x414.png" width="598" height="414" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/90f56fff-737b-4907-91e6-e46b89ffc51e_598x414.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:414,&quot;width&quot;:598,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:109976,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://x.com/readwithai/status/1875699504625893406&quot;,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!K5TK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90f56fff-737b-4907-91e6-e46b89ffc51e_598x414.png 424w, https://substackcdn.com/image/fetch/$s_!K5TK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90f56fff-737b-4907-91e6-e46b89ffc51e_598x414.png 848w, https://substackcdn.com/image/fetch/$s_!K5TK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90f56fff-737b-4907-91e6-e46b89ffc51e_598x414.png 1272w, https://substackcdn.com/image/fetch/$s_!K5TK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90f56fff-737b-4907-91e6-e46b89ffc51e_598x414.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Twitter seems to have quite a lot of bots on it. They do things like like your posts when they haven&#8217;t even read it, follow people while no actually post things, and tend to mostly be there for the link in their profile. </p><p>What&#8217;s worse reporting them is an infuriating 5 clicks and a couple of scrolls away - so while I was in a &#8220;web scraping mood&#8221; I decided to automate this using a bookmarklet - which only requires one click</p><p>To use this add this bookmarklet to your browser - then when you want to report and block someone, click on their profile page and run the bookmarklet.</p><p>To create the bookmarklet, create a new bookmark and copy and paste this for the URL:</p><pre><code>javascript:(function()%7Basync%20function%20run()%20%7B%0Afunction%20sleep(ms)%20%7B%0A%20%20%20%20return%20new%20Promise(resolve%20%3D%3E%20setTimeout(resolve%2C%20ms))%3B%0A%7D%0Adocument.evaluate('%2F%2F*%5B%40data-testid%3D%22userActions%22%5D'%2C%20document).iterateNext().click()%20%20%0Aawait%20sleep(1000)%0Adocument.evaluate('%2F%2F*%5B%40role%3D%22menuitem%22%5D%5Bdescendant%3A%3A*%5Bstarts-with(text()%2C%20%22Report%22)%5D%5D'%2C%20document).iterateNext().click()%20%20%0Aawait%20sleep(1000)%0Adocument.evaluate('%2F%2F*%5B%40role%3D%22dialog%22%5D%2Fdescendant%3A%3A*%5Btext()%3D%22Spam%22%5D%2Fancestor%3A%3Alabel%2Fdescendant%3A%3Ainput'%2C%20document).iterateNext().click()%0Aawait%20sleep(1000)%0Adocument.evaluate('%2F%2F*%5B%40role%3D%22dialog%22%5D%2Fdescendant%3A%3A*%5Btext()%3D%22Next%22%5D'%2C%20document).iterateNext().click()%0Aawait%20sleep(1000)%0Adocument.evaluate('%2F%2F*%5B%40role%3D%22dialog%22%5D%2Fdescendant%3A%3A*%5Bstarts-with(text()%2C%20%22Block%22)%5D%2Fancestor%3A%3AButton'%2C%20document).iterateNext().click()%0A%7D%0Arun()%7D)()%3B</code></pre><p>Obvious caveats: only report spam bots - you might be tempted to try this out on a random user but that would be unfair.</p><h1>Demo</h1><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tRH9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce8fb50b-350c-439c-9f7c-4ce1c6ea53ab_927x524.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tRH9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce8fb50b-350c-439c-9f7c-4ce1c6ea53ab_927x524.gif 424w, https://substackcdn.com/image/fetch/$s_!tRH9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce8fb50b-350c-439c-9f7c-4ce1c6ea53ab_927x524.gif 848w, https://substackcdn.com/image/fetch/$s_!tRH9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce8fb50b-350c-439c-9f7c-4ce1c6ea53ab_927x524.gif 1272w, https://substackcdn.com/image/fetch/$s_!tRH9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce8fb50b-350c-439c-9f7c-4ce1c6ea53ab_927x524.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tRH9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce8fb50b-350c-439c-9f7c-4ce1c6ea53ab_927x524.gif" width="927" height="524" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ce8fb50b-350c-439c-9f7c-4ce1c6ea53ab_927x524.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:524,&quot;width&quot;:927,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1656106,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tRH9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce8fb50b-350c-439c-9f7c-4ce1c6ea53ab_927x524.gif 424w, https://substackcdn.com/image/fetch/$s_!tRH9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce8fb50b-350c-439c-9f7c-4ce1c6ea53ab_927x524.gif 848w, https://substackcdn.com/image/fetch/$s_!tRH9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce8fb50b-350c-439c-9f7c-4ce1c6ea53ab_927x524.gif 1272w, https://substackcdn.com/image/fetch/$s_!tRH9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce8fb50b-350c-439c-9f7c-4ce1c6ea53ab_927x524.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h1>Details for the programmers among you</h1><p>If you want to know how this works. I use XPaths with document.evaluate and the &#8220;.click&#8221; method in JavaScript to automate all this code. There is a bit of magic with Xpath axes and the text() method to find the correct element since nothing seems to be labelled in html any more these days.</p><p>There are some &#8220;race conditions&#8221; in the code where things take a long time to run. I work around by sleeping for a period of time. A better way to do this would be to &#8220;poll&#8221; for elements to be created but this works okay as a hack.</p><pre><code>async function run() {
    function sleep(ms) {
        return new Promise(resolve =&gt; setTimeout(resolve, ms));
    }
    document.evaluate('//*[@data-testid="userActions"]', document).iterateNext().click()  
    await sleep(1000)
    document.evaluate('//*[@role="menuitem"][descendant::*[starts-with(text(), "Report")]]', document).iterateNext().click()  
    await sleep(1000)
    document.evaluate('//*[@role="dialog"]/descendant::*[text()="Spam"]/ancestor::label/descendant::input', document).iterateNext().click()
    await sleep(1000)
    document.evaluate('//*[@role="dialog"]/descendant::*[text()="Next"]', document).iterateNext().click()
    await sleep(1000)
    document.evaluate('//*[@role="dialog"]/descendant::*[starts-with(text(), "Block")]/ancestor::Button', document).iterateNext().click()
}
run()
</code></pre><h3>Related</h3><p>If you are interested in this you might be interested in <a href="https://github.com/talwrii/buffer-automation">how I automated creating twitter posts</a> from within Obsidian. </p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://readwithai.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Interesting. Maybe <a href="https://readwithai.substack.com/">check out some more posts </a>or subscribe?</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Sending code to a Python virtualenv from Emacs]]></title><description><![CDATA[@readwithai (&#120143;)]]></description><link>https://readwithai.substack.com/p/sending-code-to-a-python-virtualenv</link><guid isPermaLink="false">https://readwithai.substack.com/p/sending-code-to-a-python-virtualenv</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Thu, 02 Jan 2025 02:01:26 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!CrKo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaaa0471-ffba-4f6c-8011-7a3192410bab_801x403.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a href="https://x.com/readwithai">@readwithai</a> (<a href="https://x.comy/readwithai">&#120143;</a>)</p><p>There is a mode of developing - useful for some things - where you write individuals functions send them to an environment and test them out. This is one of the nice things about <a href="https://jupyter.org/">Jupyter</a>, and related tools such as <a href="https://orgmode.org/worg/org-contrib/babel/intro.html">org-babel</a>. However sometimes you want to write a script rather than produce a notebook, and force this is can be nice to send code from your editor while you build a script.</p><p>Emacs supports this natively with <a href="https://github.com/fgallina/python.el/blob/638b67c9497d02582b8f68f27739d2c7bf2e29fa/python.el#L2002">python-shell-send-string</a> but this works with a python shell running natively within emacs - which can be a mess with virtualenvs. Instead I decided to try hacking something up with jupyter/ ipython kernels and it seems to work reasonably well. </p><p>With this approach I start an ipython kernel in a virtual env using <strong>jupyter kernel</strong> then I find the details of this kernel by looking in <strong>~/.local/share/jupyter/runtime/ </strong> for the most recent JSON file. I wrote a tiny wrapper in python called <a href="https://gist.github.com/talwrii/464a1887aae74b35bfb9f96181655c08">jupyter-last-kernel</a> to get this file. </p><p>Then I can connect to this from within emacs using <a href="https://github.com/burakbayramli/emacs-jupyter">emacs-jupyter</a> . This has quite a lot of functionality, but I side-stepped most of this (and the setup that might come with it) by just writing this code:</p><pre><code>(defun my-jupyter-connect-last ()
  (interactive)
  (setq jupyter-current-client (jupyter-connect-repl (s-trim (shell-command-to-string "~/bins/jupytertools/jupyter-last-kernel")))))

(evil-define-operator my-jupyter-send (beg end type)
  "Evil operator to replace a region with the current kill"
  :move-point nil
  (interactive "&lt;r&gt;")
  (message (jupyter-eval-string-command (substring-no-properties (buffer-substring beg end)))))
  ; (jupyter-eval-string-command (buffer-substring beg end)))

(define-key evil-normal-state-map "gYY" 'my-jupyter-send)
(define-key evil-normal-state-map "gYc" 'my-jupyter-connect-last)


</code></pre><p>so that I can select a region and send it to this ipython kernel, and see the result. (I use the evil vim emulator here - but this code is evil adapted to a &#8220;vanilla&#8221; emacs)</p><p>You can also connect up and ipython terminal to the backend as well with <strong>jupyter console --existing $(jupyter-last-kernel) </strong>if you like. This can be useful for debugging when you want a faster evaluation loop.</p><p>Here&#8217;s an example of this in use:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CrKo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaaa0471-ffba-4f6c-8011-7a3192410bab_801x403.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CrKo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaaa0471-ffba-4f6c-8011-7a3192410bab_801x403.gif 424w, https://substackcdn.com/image/fetch/$s_!CrKo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaaa0471-ffba-4f6c-8011-7a3192410bab_801x403.gif 848w, https://substackcdn.com/image/fetch/$s_!CrKo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaaa0471-ffba-4f6c-8011-7a3192410bab_801x403.gif 1272w, https://substackcdn.com/image/fetch/$s_!CrKo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaaa0471-ffba-4f6c-8011-7a3192410bab_801x403.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CrKo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaaa0471-ffba-4f6c-8011-7a3192410bab_801x403.gif" width="801" height="403" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/caaa0471-ffba-4f6c-8011-7a3192410bab_801x403.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:403,&quot;width&quot;:801,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2896301,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CrKo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaaa0471-ffba-4f6c-8011-7a3192410bab_801x403.gif 424w, https://substackcdn.com/image/fetch/$s_!CrKo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaaa0471-ffba-4f6c-8011-7a3192410bab_801x403.gif 848w, https://substackcdn.com/image/fetch/$s_!CrKo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaaa0471-ffba-4f6c-8011-7a3192410bab_801x403.gif 1272w, https://substackcdn.com/image/fetch/$s_!CrKo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaaa0471-ffba-4f6c-8011-7a3192410bab_801x403.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p></p><p></p><p>Some caveats:</p><ul><li><p>You don&#8217;t actually need jupyter to run the kernel, you can use <strong>ipython3 kernel </strong>instead, which oddly stores its kernel file in the same place as jupyter</p></li><li><p>There used to be an <strong>ipython3 console </strong>command - but this seems to have been replaced with <strong>jupyter console </strong>which again, oddly,  connects to ipython kernels.</p></li><li><p>If you pull up a full <strong>jupyter notebook </strong>then <strong>emacs-jupyter </strong>has a command <strong>jupyter-server-list-kernels </strong>which might be useful.</p><p></p></li></ul><p></p><div><hr></div><p><em>This blog is mostly about reading, Ai and Obsidian. But if you are interested in emacs org mode you might like to read a bit about <a href="https://readwithai.substack.com/s/cookbook">Obsidian</a>. And if you are an emacs user who has moved to Obsidian you might like to read about <a href="https://readwithai.substack.com/p/obsidian-plugin-repl">Plugin REPL</a> a plugin I wrote that brings some emacs goodness to Obsidian. Or&#8230; if you interested in reading, agency and AI you might like to read the whole blog, but I would suggest <a href="https://readwithai.substack.com/p/reading-and-agency">this page</a> to get a feel.</em></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Symbolboard - An ergonomic keyboard layout (implemented on linux)]]></title><description><![CDATA[Plus how to define your own xkb keyboard layout on linux]]></description><link>https://readwithai.substack.com/p/symbolboard-an-ergonomic-keyboard</link><guid isPermaLink="false">https://readwithai.substack.com/p/symbolboard-an-ergonomic-keyboard</guid><dc:creator><![CDATA[Read with ai]]></dc:creator><pubDate>Mon, 30 Dec 2024 17:03:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!KQ0-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e01161-463e-474a-b9dc-84adb1720d14_800x565.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>@readwithai - <a href="https://x.com/readwithai">X</a> - github</p><blockquote><p><em><strong>Update</strong>: I had issues with KDE on X using using the keycode (i.e. the key that gets pressed) rather than the entered keysym which breaks keymaps with layers if there are two shortcuts with the same physical key but in differnt layers. Also wayland, a new protocol which replaces X, does not support <strong>xkbcomp </strong>as is used here (though wayland does not support many things). Together this has led me to use <strong><a href="https://github.com/rvaiya/keyd">keyd</a> </strong>to reimplement symbolboard at the kernel level. This has various benefits (and some limitiations) which I will discuss in another post.</em></p></blockquote><p></p><p>For a while, I have been using a keyboard mapping I call <a href="https://github.com/talwrii/symbolboard2">symbolboard</a> with Linux (I also created a <a href="https://github.com/talwrii/symbolboard-mac">similar key map with Karabiner for macs</a>). This was <a href="https://github.com/talwrii/symbolboard">achieved with a tool called xmodmap</a>. This is an <a href="https://github.com/talwrii/symbolboard2">ergonomic keyboard layout for linux</a> that moves all the modifiers onto the number keys.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://readwithai.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Read with ai! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>There are two aims of this keyboard mapping:</p><ol><li><p>Being less dependent on the keyboard mapping of laptops by not using the keys that tend to move (symbols and return and backspace)</p></li><li><p>Make the modifier keys easier to press</p></li></ol><p>I achieve this by moving all the modifiers to the number row where they are easy to press with both hands, and add special &#8220;shift&#8221; keys so that I can map the numbers and symbols to the letter keys with these shift keys.</p><p>This xmodmap approach worked pretty well. However I had issues with some programs like Chrome and Obsidian which acted as though I was always holding one of the modifier keys. I have managed to work around this by using xkb. In the process of this the issues seems to be something to do with &#8220;groups&#8221;, so I don&#8217;t use this using &#8220;ISO levels instead&#8221;.</p><p><strong>What this keymap looks like</strong></p><p>These are the three &#8220;levels&#8221; of my keyboard map. One is mostly for symbols and motions another is mostly for numbers, though there is a bit of overlap. I have found this quite convenient to use for a view yeras.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KQ0-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e01161-463e-474a-b9dc-84adb1720d14_800x565.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KQ0-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e01161-463e-474a-b9dc-84adb1720d14_800x565.png 424w, https://substackcdn.com/image/fetch/$s_!KQ0-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e01161-463e-474a-b9dc-84adb1720d14_800x565.png 848w, https://substackcdn.com/image/fetch/$s_!KQ0-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e01161-463e-474a-b9dc-84adb1720d14_800x565.png 1272w, https://substackcdn.com/image/fetch/$s_!KQ0-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e01161-463e-474a-b9dc-84adb1720d14_800x565.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KQ0-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e01161-463e-474a-b9dc-84adb1720d14_800x565.png" width="800" height="565" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d0e01161-463e-474a-b9dc-84adb1720d14_800x565.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:565,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!KQ0-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e01161-463e-474a-b9dc-84adb1720d14_800x565.png 424w, https://substackcdn.com/image/fetch/$s_!KQ0-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e01161-463e-474a-b9dc-84adb1720d14_800x565.png 848w, https://substackcdn.com/image/fetch/$s_!KQ0-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e01161-463e-474a-b9dc-84adb1720d14_800x565.png 1272w, https://substackcdn.com/image/fetch/$s_!KQ0-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e01161-463e-474a-b9dc-84adb1720d14_800x565.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Msmx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5cbf6843-52f6-4917-bd43-fc80dcb25040_800x565.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Msmx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5cbf6843-52f6-4917-bd43-fc80dcb25040_800x565.png 424w, https://substackcdn.com/image/fetch/$s_!Msmx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5cbf6843-52f6-4917-bd43-fc80dcb25040_800x565.png 848w, https://substackcdn.com/image/fetch/$s_!Msmx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5cbf6843-52f6-4917-bd43-fc80dcb25040_800x565.png 1272w, https://substackcdn.com/image/fetch/$s_!Msmx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5cbf6843-52f6-4917-bd43-fc80dcb25040_800x565.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Msmx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5cbf6843-52f6-4917-bd43-fc80dcb25040_800x565.png" width="800" height="565" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5cbf6843-52f6-4917-bd43-fc80dcb25040_800x565.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:565,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Msmx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5cbf6843-52f6-4917-bd43-fc80dcb25040_800x565.png 424w, https://substackcdn.com/image/fetch/$s_!Msmx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5cbf6843-52f6-4917-bd43-fc80dcb25040_800x565.png 848w, https://substackcdn.com/image/fetch/$s_!Msmx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5cbf6843-52f6-4917-bd43-fc80dcb25040_800x565.png 1272w, https://substackcdn.com/image/fetch/$s_!Msmx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5cbf6843-52f6-4917-bd43-fc80dcb25040_800x565.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NtlX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a3307b0-485b-4f1b-ad26-bcae28385ada_800x565.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NtlX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a3307b0-485b-4f1b-ad26-bcae28385ada_800x565.png 424w, https://substackcdn.com/image/fetch/$s_!NtlX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a3307b0-485b-4f1b-ad26-bcae28385ada_800x565.png 848w, https://substackcdn.com/image/fetch/$s_!NtlX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a3307b0-485b-4f1b-ad26-bcae28385ada_800x565.png 1272w, https://substackcdn.com/image/fetch/$s_!NtlX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a3307b0-485b-4f1b-ad26-bcae28385ada_800x565.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NtlX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a3307b0-485b-4f1b-ad26-bcae28385ada_800x565.png" width="800" height="565" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7a3307b0-485b-4f1b-ad26-bcae28385ada_800x565.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:565,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!NtlX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a3307b0-485b-4f1b-ad26-bcae28385ada_800x565.png 424w, https://substackcdn.com/image/fetch/$s_!NtlX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a3307b0-485b-4f1b-ad26-bcae28385ada_800x565.png 848w, https://substackcdn.com/image/fetch/$s_!NtlX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a3307b0-485b-4f1b-ad26-bcae28385ada_800x565.png 1272w, https://substackcdn.com/image/fetch/$s_!NtlX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a3307b0-485b-4f1b-ad26-bcae28385ada_800x565.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>You can use this too</strong></p><p>I&#8217;ve released this code as on <a href="https://github.com/talwrii/symbolboard2">github</a>. It should be fairly straightforward to use and edit.</p><p><strong>Interesting practicalities</strong></p><p><code>xkb</code> in practice is quite complicated, and it&#8217;s default mode of working is quite magical. <code>setxkbmap</code> interacts with rule files that specify a set of keyboard mappings that run. They are different types of keyboard mapping objects, types, symbol, geometries and compatibility. I tried to fit into this world but when I discovered that I could not unmap existing keymappings, and found it difficult to stop certain keymapping files from loading (specifically the <code>pc</code> keyboard map was always running) I decided to opt out and use <code>xkbcomp</code></p><p><code>xkbcomp</code> is a lower level system than <code>setxkbmap</code> that is used to compile keyboard maps and load them into the X server. However, it can also dump out the current keyboard map to a text file. So I used this to dump out a complete keymap, edit it, and then reload it. </p><pre><code>xkbcomp :0 map.xkb #dump
vim map.xkb #edit
xkbcomp map.xkb :0 # reload</code></pre><p>This allowed me to circumvent all the magic <code>setxkbmap</code> logic. The downside this is that my keyboard might not work with a different type of keyboard layout.  <code>setxkbmap</code>, while rather big, nested, and monolithic does provide a level of indirection allowing you to support different keyboards models &#8202;&#8212;&#8202;but the modifier logic is quite low-level and I could not work out how to override it.</p><p><strong>Related work</strong></p><p>I&#8217;ve found other people trying similar things. I know that levels are quite popular with QMK developers&#8202;&#8212;&#8202;this loads some keyboard logic onto an external mechanical keyboard. <a href="https://precondition.github.io/home-row-mods">Homerow modifiers</a> are a common approach to make modifiers easier to press and available to both hands (like I do here).</p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://readwithai.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Interesting? Get updates by email; or follow me on <a href="https://x.com/readwithai">X</a></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item></channel></rss>