<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Daniel Borek</title><description>Instrumental reflexes</description><link>https://danielborek.me/</link><language>en</language><copyright>Copyright 2026, Daniel Borek</copyright><lastBuildDate>Sun, 22 Mar 2026 00:00:00 +0000</lastBuildDate><generator>Hugo - gohugo.io</generator><docs>http://cyber.harvard.edu/rss/rss.html</docs><atom:link href="https://danielborek.me/atom.xml" rel="self" type="application/atom+xml"/><item><title>Listening to Local Music with MPD on Mac in 2026</title><link>https://danielborek.me/2026/03/2025-03-22-mpc-and-local-music-2026/</link><description>&lt;p&gt;In the era of streaming, I am returning to my local library of ripped CDs and Bandcamp albums. Going back to local is not (only) about rejecting streaming (check &lt;code&gt;Mood Machine&lt;/code&gt; by Liz Pelly, which describes some of the wrongdoing of the industry), but more about better regulating your environment in the attention economy.&lt;/p&gt;
&lt;p&gt;I was once a very avid listener and follower of blogs, recommendations from peers (kudos to &lt;code&gt;niekomercyjni muzyczni laicy&lt;/code&gt;), and in general my own exploration. But at some point, maybe due to lack of time and energy, I started relying almost completely on Spotify to tell me what to listen to from my own library or its recomendation. While it was handy, the recommendations became really similar, and the UI mostly surfaced only the latest listened playlists and albums. It became hard to make a distinction between my library and their recommendations. I ended up listening to much less of much more homogeneous music.&lt;/p&gt;
&lt;p&gt;The renaissance came when I rediscovered album views. First I read on an app proposing rethinking Apple Music (I weren&amp;rsquo;t subscriber), but then I found a similar interface for a local library via MPD, first Persephone, then Rigelian. Interfaces matter more than I expected here. With a local or remote library, a good client is not just a remote control but part of how the collection appears to you. Cover views, album browsing, and even small bits of friction change what I end up listening to.&lt;/p&gt;
&lt;p&gt;Spotify may still be nice in terms of convenience and availability of albums, setting apart the controversies, but resurfacing older music or albums is much harder there. Also, more and more Spotify and other streaming services move away from the concept of the album toward playlists. That makes sense sometimes, but I still miss the artistic completeness of albums. As a person renting a house abroad and moving often, I am still not prepared to start amassing a collection of vinyl or cassettes, so I need to focus on digital. And digital still offers a lot.&lt;/p&gt;
&lt;p&gt;I like that this setup can bring back albums I have not played in years. A local library can easily become too large and too familiar at the same time, where I mostly return to the same small corner of it. Randomizing album playback is a simple way to make the collection feel wider again, and it can still bring surprise and wonder :)&lt;/p&gt;
&lt;p&gt;I was a long-time ago a Foobar2000 user, but a while back I switched to MPD and various interfaces. On Mac and iOS, I used to enjoy Persephone for its nice cover view, though it is no longer supported since development ceased. Right now I really like Rigelian. It is written in Swift, offers a lovely album view, and has the potential to resurface random albums. It also recently received a major update with a new interface.&lt;/p&gt;
&lt;p&gt;MPD is a solution for that, and its biggest advantage is its extendability via players and the fact that it does not get in your way when you need something minimal. Rigelian is really nice, but I often prefer just to listen to music in my terminal. Sometimes I still use ncmpcpp as a command-line client, but recently I switched to &lt;code&gt;rmpc&lt;/code&gt;. It is like &lt;code&gt;ncmpcpp&lt;/code&gt;, but it offers a nice cover view out of the box, at least in the &lt;code&gt;ghostty&lt;/code&gt; terminal. The latest release from February 2026 has a bug preventing it from displaying covers in &lt;code&gt;iTerm&lt;/code&gt;, though this was fixed on &lt;code&gt;main&lt;/code&gt; and has not yet been released. I tried to make covers available in &lt;code&gt;ncmpcpp&lt;/code&gt;, but on Mac it seems to be a bit harder than on Linux, so I gave up :)&lt;/p&gt;
&lt;p&gt;To keep my metadata clean and download covers I am using &lt;a href="https://www.mp3tag.de/en/"&gt;Mp3tag&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;P.S. I still scrobble music on Last.fm, and many of my friends do too. I have been doing this since 2008, although I created a new account in 2011 and had a few years off from more active music listening practices in between. I remember the time when Last.fm felt more like a social network; now it is only a shadow of its former glory. Call me old-school, but I really enjoy documenting my listening practices, not only on Last.fm but also on Rate Your Music. It is also still a great way to peek at what your friends are listening to and what albums to try next, especially lists on Rate Your Music(the lists were hot there long before Letterboxd). Scrobbling still gives a kind of continuity to my listening, even when the software around it changes.&lt;/p&gt;
&lt;h2 id="a-bit-of-technical-setup"&gt;A bit of technical setup &lt;a href="#a-bit-of-technical-setup" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I installed &lt;code&gt;mpdscribble&lt;/code&gt; to enable scrobbling via MPD. There is also a handy command to restart services, since I installed &lt;code&gt;mpd&lt;/code&gt; and &lt;code&gt;mpdscribble&lt;/code&gt; via Homebrew:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew services restart mpd mpdscribble
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew services stop mpd mpdscribble
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When I add new albums or move them between folders, for refreshing the local mpd database I use:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mpc update --wait
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As I wrote before, one thing I have been thinking about is how much choice I have in my library, and how often that makes my attention switch too quickly. There are too many albums to choose from, so I decided to randomize the process a bit and make it easier to return to records I might otherwise overlook.&lt;/p&gt;
&lt;p&gt;Rigelian offers random albums out of the box, but when I am listening in the terminal, I am using a small script added to my .zshrc to cache albums, pick one at random, and keep a short history so I can replay earlier picks.&lt;/p&gt;
&lt;p&gt;My MPD settings in &lt;code&gt;~/.mpd/mpd.conf&lt;/code&gt; look like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-conf" data-lang="conf"&gt;bind_to_address &amp;#34;127.0.0.1&amp;#34;
bind_to_address &amp;#34;::1&amp;#34;
#bind_to_address &amp;#34;/Users/daniel/.config/mpd/socket&amp;#34;
port &amp;#34;6600&amp;#34;
audio_buffer_size &amp;#34;8192&amp;#34;
buffer_before_play &amp;#34;25%&amp;#34;
music_directory &amp;#34;/Users/daniel/Music/Moja muzyka&amp;#34;
audio_output {
type &amp;#34;osx&amp;#34;
name &amp;#34;Mac Output&amp;#34;
mixer_type &amp;#34;software&amp;#34;
audio_output_format &amp;#34;44100:16:2&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#57606a"&gt;# part of .zshrc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#953800"&gt;album_cache_file&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$HOME&lt;/span&gt;&lt;span style="color:#0a3069"&gt;/.cache/mpd-albums.tsv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#953800"&gt;album_history_file&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$HOME&lt;/span&gt;&lt;span style="color:#0a3069"&gt;/.cache/mpd-last-album.tsv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rebuild_album_cache&lt;span style="color:#0550ae"&gt;()&lt;/span&gt; &lt;span style="color:#0550ae"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mkdir -p &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#0a3069"&gt;${&lt;/span&gt;&lt;span style="color:#953800"&gt;album_cache_file&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#953800"&gt;h&lt;/span&gt;&lt;span style="color:#0a3069"&gt;}&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mpc -f &lt;span style="color:#0a3069"&gt;&amp;#39;[[%albumartist%]|[%artist%]]\t[%album%]&amp;#39;&lt;/span&gt; search &lt;span style="color:#0a3069"&gt;&amp;#39;(album != &amp;#34;&amp;#34;)&amp;#39;&lt;/span&gt; &lt;span style="color:#1f2328"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; awk -F &lt;span style="color:#0a3069"&gt;&amp;#39;\t&amp;#39;&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#39;NF &amp;gt;= 2 &amp;amp;&amp;amp; $1 != &amp;#34;&amp;#34; &amp;amp;&amp;amp; $2 != &amp;#34;&amp;#34; &amp;amp;&amp;amp; !seen[$1 FS $2]++ { print $1 FS $2 }&amp;#39;&lt;/span&gt; &lt;span style="color:#0a3069"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;gt; &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$album_cache_file&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6639ba"&gt;printf&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#39;Cached %s albums\n&amp;#39;&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#cf222e"&gt;$(&lt;/span&gt;wc -l &amp;lt; &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$album_cache_file&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#1f2328"&gt;|&lt;/span&gt; tr -d &lt;span style="color:#0a3069"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span style="color:#cf222e"&gt;)&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;random_album_cached&lt;span style="color:#0550ae"&gt;()&lt;/span&gt; &lt;span style="color:#0550ae"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;[&lt;/span&gt; -s &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$album_cache_file&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#0550ae"&gt;]&lt;/span&gt; &lt;span style="color:#0550ae"&gt;||&lt;/span&gt; rebuild_album_cache
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6639ba"&gt;local&lt;/span&gt; choice artist album
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#953800"&gt;choice&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#cf222e"&gt;$(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; python3 - &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$album_cache_file&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;lt;&amp;lt;&amp;#39;PY&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt;import random, sys, pathlib
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt;p = pathlib.Path(sys.argv[1])
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt;rows = [line.rstrip(&amp;#34;\n&amp;#34;) for line in p.read_text(encoding=&amp;#34;utf-8&amp;#34;).splitlines() if line.strip()]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt;print(random.choice(rows) if rows else &amp;#34;&amp;#34;, end=&amp;#34;&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt;PY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cf222e"&gt;)&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;[&lt;/span&gt; -z &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$choice&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#0550ae"&gt;]&lt;/span&gt; &lt;span style="color:#0550ae"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#0550ae"&gt;{&lt;/span&gt; &lt;span style="color:#6639ba"&gt;echo&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;No albums found.&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;;&lt;/span&gt; &lt;span style="color:#cf222e"&gt;return&lt;/span&gt; 1&lt;span style="color:#1f2328"&gt;;&lt;/span&gt; &lt;span style="color:#0550ae"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#953800"&gt;artist&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#0a3069"&gt;${&lt;/span&gt;&lt;span style="color:#953800"&gt;choice&lt;/span&gt;&lt;span style="color:#1f2328"&gt;%%&lt;/span&gt;&lt;span style="color:#0a3069"&gt;$&amp;#39;\t&amp;#39;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;*&lt;/span&gt;&lt;span style="color:#0a3069"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#953800"&gt;album&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#0a3069"&gt;${&lt;/span&gt;&lt;span style="color:#953800"&gt;choice&lt;/span&gt;&lt;span style="color:#1f2328"&gt;#*&lt;/span&gt;&lt;span style="color:#0a3069"&gt;$&amp;#39;\t&amp;#39;&lt;/span&gt;&lt;span style="color:#0a3069"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6639ba"&gt;printf&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#39;Playing: %s — %s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$artist&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$album&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mpc clear
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mpc findadd albumartist &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$artist&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; album &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$album&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#0550ae"&gt;||&lt;/span&gt; &lt;span style="color:#0a3069"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mpc findadd artist &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$artist&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; album &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$album&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6639ba"&gt;printf&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#39;%s\t%s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$artist&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$album&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$album_history_file&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mpc play
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#57606a"&gt;# Updated replay function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;replay_album&lt;span style="color:#0550ae"&gt;()&lt;/span&gt; &lt;span style="color:#0550ae"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;[&lt;/span&gt; -s &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$album_history_file&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#0550ae"&gt;]&lt;/span&gt; &lt;span style="color:#0550ae"&gt;||&lt;/span&gt; &lt;span style="color:#0550ae"&gt;{&lt;/span&gt; &lt;span style="color:#6639ba"&gt;echo&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;No album history.&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;;&lt;/span&gt; &lt;span style="color:#cf222e"&gt;return&lt;/span&gt; 1&lt;span style="color:#1f2328"&gt;;&lt;/span&gt; &lt;span style="color:#0550ae"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6639ba"&gt;local&lt;/span&gt; &lt;span style="color:#953800"&gt;n&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#0a3069"&gt;${&lt;/span&gt;&lt;span style="color:#953800"&gt;1&lt;/span&gt;&lt;span style="color:#cf222e"&gt;:-&lt;/span&gt;&lt;span style="color:#953800"&gt;1&lt;/span&gt;&lt;span style="color:#0a3069"&gt;}&lt;/span&gt; total line artist album
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#953800"&gt;total&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#cf222e"&gt;$(&lt;/span&gt;wc -l &amp;lt; &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$album_history_file&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#1f2328"&gt;|&lt;/span&gt; tr -d &lt;span style="color:#0a3069"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span style="color:#cf222e"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;((&lt;/span&gt; n &amp;gt; total &lt;span style="color:#0550ae"&gt;))&lt;/span&gt; &lt;span style="color:#0550ae"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#953800"&gt;n&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#953800"&gt;$total&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#953800"&gt;line&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#cf222e"&gt;$(&lt;/span&gt;tail -n &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$n&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$album_history_file&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#1f2328"&gt;|&lt;/span&gt; head -n 1&lt;span style="color:#cf222e"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#953800"&gt;artist&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#0a3069"&gt;${&lt;/span&gt;&lt;span style="color:#953800"&gt;line&lt;/span&gt;&lt;span style="color:#1f2328"&gt;%%&lt;/span&gt;&lt;span style="color:#0a3069"&gt;$&amp;#39;\t&amp;#39;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;*&lt;/span&gt;&lt;span style="color:#0a3069"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#953800"&gt;album&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#0a3069"&gt;${&lt;/span&gt;&lt;span style="color:#953800"&gt;line&lt;/span&gt;&lt;span style="color:#1f2328"&gt;#*&lt;/span&gt;&lt;span style="color:#0a3069"&gt;$&amp;#39;\t&amp;#39;&lt;/span&gt;&lt;span style="color:#0a3069"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6639ba"&gt;printf&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#39;Replaying (%d ago): %s — %s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$n&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$artist&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$album&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mpc clear
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mpc findadd albumartist &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$artist&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; album &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$album&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#0550ae"&gt;||&lt;/span&gt; &lt;span style="color:#0a3069"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mpc findadd artist &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$artist&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt; album &lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#953800"&gt;$album&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mpc play
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6639ba"&gt;alias&lt;/span&gt; &lt;span style="color:#953800"&gt;rra&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#39;replay_album&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6639ba"&gt;alias&lt;/span&gt; &lt;span style="color:#953800"&gt;ra&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#39;random_album_cached&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description><author>dtborek@gmail.com (Daniel Borek)</author><guid>https://danielborek.me/2026/03/2025-03-22-mpc-and-local-music-2026/</guid><pubDate>Sun, 22 Mar 2026 00:00:00 +0000</pubDate></item><item><title>Tracking commits across multiple repositories with Beeminder</title><link>https://danielborek.me/2026/03/2025-03-07-beeminder-github-integration/</link><description>&lt;p&gt;&lt;a href="https://www.beeminder.com/"&gt;Beeminder&lt;/a&gt; is a habit-tracking tool built around committing to measurable progress over time. You can even put real money on the line, so missing your target comes with a financial sting — and for some people, that extra bit of accountability is surprisingly effective. It also integrates with various services, which makes it easy to track progress automatically instead of logging everything by hand. For programming, tracking habits like commits can help maintain momentum and make it easier to notice when you are quietly drifting away from a project.&lt;/p&gt;
&lt;p&gt;Beeminder used to have an option to track commits across your whole GitHub account, but for new goals that is not really available anymore. The simple workaround is to create one &lt;strong&gt;Do More&lt;/strong&gt; goal and let each repository send commit counts to it.&lt;/p&gt;
&lt;p&gt;It sounds more complicated than it is. In practice, you create one Beeminder goal, add three secrets to each repository, and drop the same GitHub Actions workflow into all of them.&lt;/p&gt;
&lt;h2 id="create-a-do-more-goal"&gt;Create a Do More goal &lt;a href="#create-a-do-more-goal" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Create a normal &lt;strong&gt;Do More&lt;/strong&gt; goal in Beeminder.&lt;/p&gt;
&lt;p&gt;I used:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;goal slug: &lt;code&gt;github-commits&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;units: &lt;code&gt;commits&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then go to Beeminder account settings and copy your API token from &lt;strong&gt;Apps &amp;amp; API&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="add-secrets-to-each-repository"&gt;Add secrets to each repository &lt;a href="#add-secrets-to-each-repository" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In every repository, go to:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Settings -&amp;gt; Secrets and variables -&amp;gt; Actions -&amp;gt; New repository secret&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Add these three secrets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;BEEMINDER_USERNAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BEEMINDER_TOKEN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BEEMINDER_GOAL&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For &lt;code&gt;BEEMINDER_GOAL&lt;/code&gt;, use your goal slug, for example &lt;code&gt;github-commits&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="add-the-workflow"&gt;Add the workflow &lt;a href="#add-the-workflow" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Create &lt;code&gt;.github/workflows/beeminder-commits.yml&lt;/code&gt; in each repository:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;name&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;Send commits to Beeminder&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;on&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;push&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;jobs&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;beemind&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;runs-on&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;ubuntu-latest&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;steps&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;- &lt;span style="color:#0550ae"&gt;name&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;Send commit count to Beeminder&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;env&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;BEEMINDER_USERNAME&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;${{ secrets.BEEMINDER_USERNAME }}&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;BEEMINDER_TOKEN&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;${{ secrets.BEEMINDER_TOKEN }}&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;BEEMINDER_GOAL&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;${{ secrets.BEEMINDER_GOAL }}&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;COMMITS_JSON&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;${{ toJson(github.event.commits) }}&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;REPO&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;${{ github.repository }}&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;SHA&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;${{ github.sha }}&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;REF&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;${{ github.ref }}&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;run&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#1f2328"&gt;|&lt;/span&gt;&lt;span style="color:#0a3069"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; count=$(python - &amp;lt;&amp;lt;&amp;#39;PY&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; import json, os
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; data = os.environ.get(&amp;#34;COMMITS_JSON&amp;#34;, &amp;#34;[]&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; try:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; commits = json.loads(data)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; print(len(commits))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; except Exception:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; print(0)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; PY
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; if [ &amp;#34;$count&amp;#34; -eq 0 ]; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; echo &amp;#34;No commits to send&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; exit 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; curl -sS -X POST &amp;#34;https://www.beeminder.com/api/v1/users/${BEEMINDER_USERNAME}/goals/${BEEMINDER_GOAL}/datapoints.json&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; -d &amp;#34;auth_token=${BEEMINDER_TOKEN}&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; -d &amp;#34;value=${count}&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; -d &amp;#34;comment=${REPO} ${REF} ${SHA}&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; -d &amp;#34;requestid=${REPO}-${SHA}&amp;#34;&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That is all the plumbing. Every time you push, GitHub Actions runs the workflow, counts the commits in that push, and sends that number to the same Beeminder goal.&lt;/p&gt;
&lt;h2 id="a-small-note"&gt;A small note &lt;a href="#a-small-note" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This counts commits in the push payload. For normal use that is fine. If you work across several repositories, this gives you one combined Beeminder graph without depending on the old GitHub integration.&lt;/p&gt;
&lt;p&gt;If you only want to count pushes on &lt;code&gt;main&lt;/code&gt;, change this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;on&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;push&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;branches&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;- main&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="option-2-count-only-your-own-commits"&gt;Option 2: Count only your own commits &lt;a href="#option-2-count-only-your-own-commits" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you work in shared repositories, you may want to count only commits authored by you. In that case, you can filter commits by the email stored in your Git metadata. I have not battle-tested this setup yet in a repository with more active contributors, so treat it as a proposition of a workaround rather than something fully verified under shared-repo conditions.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;name&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;Send my commits to Beeminder&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;on&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;push&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;jobs&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;beemind&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;runs-on&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;ubuntu-latest&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;steps&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;- &lt;span style="color:#0550ae"&gt;name&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;Check out repository&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;uses&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;actions/checkout@v4&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;with&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;fetch-depth&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;0&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;- &lt;span style="color:#0550ae"&gt;name&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;Send only my commit count to Beeminder&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;env&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;AUTHOR_EMAIL&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;your.email@example.com&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;BEFORE&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;${{ github.event.before }}&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;AFTER&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;${{ github.event.after }}&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;BEEMINDER_USERNAME&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;${{ secrets.BEEMINDER_USERNAME }}&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;BEEMINDER_TOKEN&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;${{ secrets.BEEMINDER_TOKEN }}&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;BEEMINDER_GOAL&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;${{ secrets.BEEMINDER_GOAL }}&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;REPO&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;${{ github.repository }}&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;SHA&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;${{ github.sha }}&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;REF&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;${{ github.ref }}&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;run&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#1f2328"&gt;|&lt;/span&gt;&lt;span style="color:#0a3069"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; count=$(git log --format=&amp;#39;%ae&amp;#39; &amp;#34;${BEFORE}..${AFTER}&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; | awk -v author=&amp;#34;$AUTHOR_EMAIL&amp;#34; &amp;#39;$0 == author {c++} END {print c+0}&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; if [ &amp;#34;$count&amp;#34; -eq 0 ]; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; echo &amp;#34;No matching authored commits to send&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; exit 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; curl -sS -X POST &amp;#34;https://www.beeminder.com/api/v1/users/${BEEMINDER_USERNAME}/goals/${BEEMINDER_GOAL}/datapoints.json&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; -d &amp;#34;auth_token=${BEEMINDER_TOKEN}&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; -d &amp;#34;value=${count}&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; -d &amp;#34;comment=${REPO} ${REF} ${SHA}&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; -d &amp;#34;requestid=${REPO}-${SHA}&amp;#34;&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To find the email you should put in &lt;code&gt;AUTHOR_EMAIL&lt;/code&gt;, check the one Git actually uses in your commits rather than only the one stored in your general config. In a repository, you can run &lt;code&gt;git log -1 --format='%ae'&lt;/code&gt; to print the author email from your most recent commit, which is usually the safest value to use here. You can also inspect your configured email with &lt;code&gt;git config user.email&lt;/code&gt; for the current repository or &lt;code&gt;git config --global user.email&lt;/code&gt; for your global Git setup.&lt;/p&gt;</description><author>dtborek@gmail.com (Daniel Borek)</author><guid>https://danielborek.me/2026/03/2025-03-07-beeminder-github-integration/</guid><pubDate>Sat, 07 Mar 2026 00:00:00 +0000</pubDate></item><item><title>Connecting Zotero to AI Assistants via MCP</title><link>https://danielborek.me/2026/zotero-mcp-ai/</link><description>&lt;p&gt;If you use &lt;a href="https://www.zotero.org/"&gt;Zotero&lt;/a&gt; for managing your research library, you might want to talk to your papers using an AI assistant. The &lt;a href="https://modelcontextprotocol.io/introduction"&gt;Model Context Protocol (MCP)&lt;/a&gt; makes this possible — your AI can search, summarize, and analyze your local Zotero library, and if you use a local model, you can do it without uploading anything to third-party servers. Or you can use semantic search with &lt;a href="https://github.com/introfini/ZotSeek"&gt;ZotSeek&lt;/a&gt; directly within the Zotero desktop app to resurface relevant papers hidden in your library and get better semantic search when the keyword isn&amp;rsquo;t in the title or abstract.&lt;/p&gt;
&lt;p&gt;Disclaimer: to get some of the setup running, I used a debugging session with a chatbot, and a big part of this post was first dictated by me, then converted from speech to text with an open model (Parakeet on MacWhisper), cleaned via chatbot, and finally checked and edited by me (with some additional edits after I received feedback and tried to help some friends with install).&lt;/p&gt;
&lt;h2 id="why-use-your-own-library"&gt;Why use your own library? &lt;a href="#why-use-your-own-library" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There is a risk of chatbots hallucinating when you work in research — and one of the worst kinds is hallucinated bibliography. The AI just makes up references that don&amp;rsquo;t exist. The best way to limit this is to not rely on internet sources you can&amp;rsquo;t verify, but to ground the conversation in your own bibliography instead. Especially when you&amp;rsquo;ve accumulated several thousand papers over your whole research career — sometimes you forget you have something relevant sitting in your library. This tutorial is about chatting with your own curated database of publications. You still need to be careful and verify what the AI says, but you can really limit the risk of total hallucination by grounding it in your own selection of relevant papers. It&amp;rsquo;s useful when the topic you&amp;rsquo;re working on connects to things you already collected — you ask for context and it pulls from what you know is real.&lt;/p&gt;
&lt;p&gt;Even with your own library as the source, the AI can still get creative with what it attributes to your papers — it connects things very well, sometimes too well, adding claims that are only loosely present in the original text. Please read carefully and verify any &amp;lsquo;discoveries&amp;rsquo; made by your AI companion :)&lt;/p&gt;
&lt;h2 id="what-is-free-what-costs-money"&gt;What is free, what costs money &lt;a href="#what-is-free-what-costs-money" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With Zotero MCP, the local part — indexing and searching your library (if you use open-source embeddings) — is completely free. What costs money is AI inference, meaning the chatbot subscription you use to actually have a conversation (Claude, ChatGPT, Mistral, etc.). Similarly, ZotSeek does the retrieval and semantic search part entirely for free and locally — after the initial indexing, it works without any additional computation; you only need to re-index when you add new PDFs to your library (I wrote more details about the setup of it in the last sections of this blogposs)&lt;/p&gt;
&lt;p&gt;So in practice: the search/RAG layer is free, the chat layer requires a subscription or your local compute resources. But many of you probably already have one — you&amp;rsquo;d just use tokens from your existing plan a bit faster.&lt;/p&gt;
&lt;p&gt;Personally, I use Claude — the setup is the easiest. But I also wanted to test a European alternative, so I set it up with Mistral Le Chat as well. If you&amp;rsquo;re a student, Mistral has a cheap subscription around €7. The quality is a bit below the latest biggest models from Anthropic or OpenAI, but you can still get a lot of value from it. The Mistral setup is more convoluted though — I figured it out together with Gemini, so I put more details in that section below.&lt;/p&gt;
&lt;p&gt;A word on token budgets: if you&amp;rsquo;re on Claude Pro, the bigger models (like Opus with extended thinking) eat through your tokens much faster than Sonnet. In my experience, a Pro subscription gives me roughly 1.5 hours of intensive work with Opus before hitting the limit. The allowance resets every 5 hours, so I plan my work in cycles — a focused session in the morning, then another after the reset, and so on. On a free plan, expect a smaller token budget; also take into account the weekly token budget, otherwise you will quickly hit the wall.&lt;/p&gt;
&lt;p&gt;(Edit 2026-02-26) There is always the possibility to work with any tool or model that adheres to and supports MCP standards. I connected to my Zotero via the same MCP while using LM Studio on my Mac and local models (this time Qwen3.5-35B in MLX format, but it should also work with smaller models and other tools). I now do less complex searches or tasks with my local LLM to spare budget for more complex queries in Claude when I need them. There are obviously some limitations, but for clearly defined tasks local inference is pretty useful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A word on security:&lt;/strong&gt; when using the Mistral/ngrok setup, be careful not to expose your computer to the outside. Always use a password and &lt;strong&gt;do not share your ngrok links&lt;/strong&gt; with anyone.&lt;/p&gt;
&lt;p&gt;One more thing — there&amp;rsquo;s also &lt;a href="https://github.com/lifan0127/ai-research-assistant"&gt;ARIA&lt;/a&gt;, a plugin that lets you chat with your PDFs directly inside the Zotero application using your own API keys. It costs additional tokens and I haven&amp;rsquo;t used it much myself, but if you want to stay entirely within Zotero, check it out.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m also maintaining a &lt;a href="https://github.com/stars/danieltomasz/lists/zotero-plugins"&gt;curated list of useful Zotero plugins&lt;/a&gt; on GitHub — I&amp;rsquo;ll be adding new ones as I discover them.&lt;/p&gt;
&lt;p&gt;I also assume you have minimal experience with the command line and Python installation; instead of &lt;code&gt;uv&lt;/code&gt;, you can install the MCP server via &lt;code&gt;pipx&lt;/code&gt; or another way to make it available in your environment. This setup is tested on macOS, but in principle it should work on other OSes (I didn&amp;rsquo;t test it; the main difference I can think of might be path handling between OSes).&lt;/p&gt;
&lt;p&gt;It will also serve as a reminder for me — every time there is a new ngrok instance, you need to repeat the Mistral setup.&lt;/p&gt;
&lt;p&gt;Zotero MCP is available only locally via the Claude desktop app. I am not related to the development of any of these tools — you can always ask me questions, but if you find a bug or something is unclear in the documentation, let the developers know via a GitHub issue (and thank them ;)).&lt;/p&gt;
&lt;h2 id="install-zotero-mcp"&gt;Install Zotero-MCP &lt;a href="#install-zotero-mcp" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The tool we need is &lt;a href="https://github.com/54yyyu/zotero-mcp"&gt;zotero-mcp&lt;/a&gt; (&lt;a href="https://stevenyuyy.us/zotero-mcp/index.html"&gt;documentation&lt;/a&gt;). It works with Claude Desktop, ChatGPT, Mistral Le Chat, and other MCP-compatible clients.&lt;/p&gt;
&lt;h3 id="prerequisites"&gt;Prerequisites &lt;a href="#prerequisites" class="hash"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zotero 7+&lt;/strong&gt; running on your computer&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Python&lt;/strong&gt; installed (with &lt;code&gt;uv&lt;/code&gt; package manager recommended)&lt;/li&gt;
&lt;li&gt;In Zotero: go to &lt;strong&gt;Settings &amp;gt; Advanced&lt;/strong&gt; and ensure &lt;strong&gt;&amp;ldquo;Allow other applications on this computer to communicate with Zotero&amp;rdquo;&lt;/strong&gt; is &lt;strong&gt;checked&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="install-mcp"&gt;Install MCP &lt;a href="#install-mcp" class="hash"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; You can either &lt;a href="https://docs.astral.sh/uv/getting-started/installation/"&gt;install uv&lt;/a&gt; directly or with the help of &lt;code&gt;brew&lt;/code&gt;. &lt;code&gt;brew&lt;/code&gt; is a community package manager for macOS: &lt;a href="https://brew.sh/"&gt;https://brew.sh/&lt;/a&gt;. For both methods, you need to use Terminal. If you haven&amp;rsquo;t used Terminal before, you can open it on Mac with Option + Space and search for &amp;ldquo;Terminal&amp;rdquo;. The easiest install command is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;uv tool install zotero-mcp-server
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;zotero-mcp setup &lt;span style="color:#57606a"&gt;# Auto-configure (Claude Desktop supported)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;During the setup you will have occasion to configure the semantic search.
After setup, initialize your search database:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#57606a"&gt;# Build with full-text extraction (slower, more comprehensive)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;zotero-mcp update-db --fulltext
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="optional-enable-semantic-search"&gt;Optional: Enable Semantic Search &lt;a href="#optional-enable-semantic-search" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For AI-powered similarity search across your library (beyond basic keyword matching):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;zotero-mcp setup --semantic-config-only
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;zotero-mcp update-db --fulltext
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;--fulltext&lt;/code&gt; flag indexes the full text of your PDFs, which is slower but gives much better results. Without it, only metadata is indexed.&lt;/p&gt;
&lt;p&gt;For more details and more options check the github repository of the MCP and &lt;a href="https://github.com/54yyyu/zotero-mcp#-semantic-search"&gt;semantic search section&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="setup-for-claude-desktop-recommended"&gt;Setup for Claude Desktop (recommended) &lt;a href="#setup-for-claude-desktop-recommended" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you installed and have run the command&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;zotero-mcp setup &lt;span style="color:#57606a"&gt;# Auto-configure (Claude Desktop supported)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;you&amp;rsquo;ve alreade done that.
This auto-configures your &lt;code&gt;claude_desktop_config.json&lt;/code&gt;. Restart Claude Desktop and you&amp;rsquo;re done — Zotero tools will appear in your conversation.&lt;/p&gt;
&lt;p&gt;If you prefer manual config, add this to your &lt;code&gt;claude_desktop_config.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;mcpServers&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;zotero&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;zotero-mcp&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;env&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;ZOTERO_LOCAL&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Setup for Claude code (assuming mcp server is instlled already):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;claude mcp add --scope user zotero ~/.local/bin/zotero-mcp &lt;span style="color:#0a3069"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -e &lt;span style="color:#953800"&gt;ZOTERO_LOCAL&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#6639ba"&gt;true&lt;/span&gt; &lt;span style="color:#0a3069"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -e &lt;span style="color:#953800"&gt;ZOTERO_EMBEDDING_MODEL&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;default
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;then verify it via&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;claude mcp list
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In a fresh session (or after /mcp reconnect), the Zotero tools become available.
In case of issues use absolute path to your user instead of &lt;code&gt;~&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="qwen-desktopstudio"&gt;Qwen Desktop/Studio &lt;a href="#qwen-desktopstudio" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Assuming you have uv installed and configure zotero-mcp in other steps, add this directly to the qwen app (tested on MacOS only)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;mcpServers&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;zotero&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;uvx&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#1f2328"&gt;[&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;zotero-mcp&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;env&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;ZOTERO_LOCAL&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="setup-for-chatgpt"&gt;Setup for ChatGPT &lt;a href="#setup-for-chatgpt" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;According to the MCP documentation, ChatGPT also supports MCP now, but the MCP requires a slightly different configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;uv tool install zotero-mcp-server
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;zotero-mcp setup --no-claude
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;--no-claude&lt;/code&gt; flag creates a standalone configuration optimized for ChatGPT&amp;rsquo;s specific tool naming requirements.&lt;/p&gt;
&lt;p&gt;Other option is to use OpenAI codex directly with you installed MCP&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;codex mcp add zotero &lt;span style="color:#0a3069"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --env &lt;span style="color:#953800"&gt;ZOTERO_LOCAL&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#6639ba"&gt;true&lt;/span&gt; &lt;span style="color:#0a3069"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --env &lt;span style="color:#953800"&gt;ZOTERO_EMBEDDING_MODEL&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;default &lt;span style="color:#0a3069"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -- /Users/daniel/.local/bin/zotero-mcp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the just have conversation in termin in mode &lt;code&gt;/plan&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="setup-for-mistral-le-chat"&gt;Setup for Mistral Le Chat &lt;a href="#setup-for-mistral-le-chat" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Mistral Le Chat supports MCP via remote connectors, which means you need to expose your local MCP server through a secure tunnel. This section covers both the one-time setup and the daily workflow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The mental model&lt;/strong&gt;: Your Zotero library is a locked room. The MCP Server is the librarian inside. ngrok punches a temporary (or permanent) tunnel through your firewall so Mistral can talk to the librarian.&lt;/p&gt;
&lt;p&gt;You will need ngrok installed and authenticated — we cover that first.&lt;/p&gt;
&lt;h3 id="step-0-install-ngrok-and-claim-your-permanent-address"&gt;Step 0: Install ngrok and Claim Your Permanent Address &lt;a href="#step-0-install-ngrok-and-claim-your-permanent-address" class="hash"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;By default, ngrok gives you a random address that changes every time you restart it, which means re-configuring Mistral every day. To avoid this, claim one free permanent domain — you only do this once.&lt;/p&gt;
&lt;p&gt;Install ngrok (using the same brew tool as before):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install --cask ngrok
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Connect your account: Go to ngrok.com, sign up for a free account, and paste the auth command it gives you:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ngrok config add-authtoken YOUR_PERSONAL_TOKEN_HERE
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Optional — Claim a permanent domain:&lt;/strong&gt; By default, ngrok gives you a random URL that changes every time you restart it, which means updating Mistral&amp;rsquo;s connector every day. To avoid this, log into your &lt;a href="https://dashboard.ngrok.com/cloud-edge/domains"&gt;ngrok Dashboard&lt;/a&gt;, click &lt;strong&gt;&amp;ldquo;Create Domain&amp;rdquo;&lt;/strong&gt;, and copy the free address it gives you (e.g. &lt;code&gt;fancy-shrimp-1.ngrok-free.app&lt;/code&gt;). This becomes your permanent &amp;ldquo;phone number.&amp;rdquo;&lt;/p&gt;
&lt;h3 id="step-1-start-the-mcp-server"&gt;Step 1: Start the MCP Server &lt;a href="#step-1-start-the-mcp-server" class="hash"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;TThis section assumes you have already installed and configured &lt;code&gt;zotero-mcp&lt;/code&gt; on your computer using previous step. Open your terminal and run the server in SSE mode:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;uv tool run zotero-mcp serve --transport sse --port &lt;span style="color:#0550ae"&gt;8000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Keep this terminal open.&lt;/p&gt;
&lt;h3 id="step-2-open-a-secure-tunnel"&gt;Step 2: Open a Secure Tunnel &lt;a href="#step-2-open-a-secure-tunnel" class="hash"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Open a &lt;strong&gt;new&lt;/strong&gt; terminal tab and run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ngrok http &lt;span style="color:#0550ae"&gt;8000&lt;/span&gt; --basic-auth&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#39;admin:your-password&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;(Use &lt;strong&gt;single quotes&lt;/strong&gt; &lt;code&gt;'&lt;/code&gt; around the credentials to prevent Zsh errors)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You will see a deprecation warning — this is expected, the flag still works. Copy the &lt;strong&gt;Forwarding URL&lt;/strong&gt; from the output (e.g. &lt;code&gt;https://xyz-123.ngrok-free.app&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;If you claimed a permanent domain in Step 0, use instead:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ngrok http &lt;span style="color:#0550ae"&gt;8000&lt;/span&gt; --url&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;YOUR-DOMAIN.ngrok-free.app --basic-auth&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#39;admin:your-password&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="step-3-get-your-auth-token"&gt;Step 3: Get Your Auth Token &lt;a href="#step-3-get-your-auth-token" class="hash"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Mistral needs your credentials encoded in Base64. Run this in a &lt;strong&gt;third&lt;/strong&gt; terminal tab:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6639ba"&gt;echo&lt;/span&gt; -n &lt;span style="color:#0a3069"&gt;&amp;#39;admin:your-password&amp;#39;&lt;/span&gt; &lt;span style="color:#1f2328"&gt;|&lt;/span&gt; base64
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Copy the output string (e.g. &lt;code&gt;YWRtaW46eW91ci1wYXNzd29yZA==&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id="step-4-configure-mistral-le-chat"&gt;Step 4: Configure Mistral Le Chat &lt;a href="#step-4-configure-mistral-le-chat" class="hash"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Go to &lt;a href="https://chat.mistral.ai/"&gt;Mistral Le Chat&lt;/a&gt; &amp;gt; &lt;strong&gt;Connectors&lt;/strong&gt; (plug icon) &amp;gt; &lt;strong&gt;+ Add Connector&lt;/strong&gt; &amp;gt; &lt;strong&gt;Custom MCP Connector&lt;/strong&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Field&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Setting&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Connector Server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://YOUR-NGROK-URL/sse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Must&lt;/strong&gt; end in &lt;code&gt;/sse&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;API Token Authentication&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Header Name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Authorization&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Header Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Basic&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Select from the dropdown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Header Value&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;YWRtaW46c2VjcmV0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Paste &lt;strong&gt;only&lt;/strong&gt; the Base64 code&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="daily-workflow"&gt;Daily Workflow &lt;a href="#daily-workflow" class="hash"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Every time you want to chat with your papers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open Terminal → run the &lt;strong&gt;MCP Server&lt;/strong&gt; command (Step 1).&lt;/li&gt;
&lt;li&gt;Open a second tab → run the &lt;strong&gt;ngrok&lt;/strong&gt; command (Step 2).&lt;/li&gt;
&lt;li&gt;Open Mistral and start chatting.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you set up a permanent domain, you never have to touch the Mistral connector again. If not, you will need to repeat Steps 3 and 4 each time your ngrok URL changes.&lt;/p&gt;
&lt;h3 id="troubleshooting-mistral"&gt;Troubleshooting (Mistral) &lt;a href="#troubleshooting-mistral" class="hash"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&amp;ldquo;404 Not Found&amp;rdquo;&lt;/strong&gt; — You forgot to add &lt;code&gt;/sse&lt;/code&gt; to the end of the URL.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;ldquo;401 Unauthorized&amp;rdquo;&lt;/strong&gt; — The Base64 string doesn&amp;rsquo;t match the credentials you passed to &lt;code&gt;--basic-auth&lt;/code&gt;. Re-run the &lt;code&gt;echo&lt;/code&gt; command and paste the fresh output. Or you typed &amp;ldquo;Basic&amp;rdquo; twice (once in the dropdown, once in the text box).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;ldquo;MCP connection requires additional information&amp;hellip;&amp;rdquo;&lt;/strong&gt; — Usually a malformed URL or header. Try the nuclear option below.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The &amp;ldquo;Nuclear Option&amp;rdquo;:&lt;/strong&gt; If the dropdown menus are fighting you, bypass them by putting the credentials directly in the URL. Set Authentication to &lt;strong&gt;None&lt;/strong&gt; and URL to: &lt;code&gt;https://admin:secret@your-ngrok-url.ngrok-free.app/sse&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="bonus-how-to-use-zotseek-directly-in-zotero"&gt;Bonus: How to use ZotSeek directly in Zotero? &lt;a href="#bonus-how-to-use-zotseek-directly-in-zotero" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;ZotSeek uses semantic search, which means you can find similar papers by meaning, not just keywords. It is 100% local, and no data leaves your machine.&lt;/p&gt;
&lt;p&gt;There is a pretty neat &lt;a href="https://github.com/introfini/ZotSeek#index-your-library"&gt;explanation in the project GitHub README&lt;/a&gt;, but if you are new to more advanced use of Zotero, you need to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download the plugin.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Go to the &lt;a href="https://github.com/introfini/ZotSeek/releases"&gt;project releases page&lt;/a&gt; and download the latest file with the &lt;code&gt;.xpi&lt;/code&gt; extension.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;Install that extension as a plugin in Zotero via Tools (you can find it in the Zotero menu, on the same level as File, Edit, etc.), then pick &lt;code&gt;Plugins&lt;/code&gt;. In the upper-right corner, click the small cog button, select &lt;code&gt;Install plugin from file&lt;/code&gt;, and then enable the plugin via the toggle by its name.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After enabling it, you can play with its settings (you need to go to Zotero settings; the plugin section will be just below the Zotero sections together with other plugins).&lt;/p&gt;
&lt;p&gt;&lt;img src="zotseek_view.png" alt="" loading="lazy" /&gt;
&lt;/p&gt;
&lt;p&gt;I advise first picking the &lt;code&gt;Abstract only&lt;/code&gt; option for indexing, and automatically indexing new items.
You can also run the &amp;ldquo;Update index&amp;rdquo; action (located at the bottom of the settings pane; you will probably need to scroll to it) to index unindexed items in your library.&lt;/p&gt;
&lt;p&gt;I did indexing in abstract-only mode — 1 chunk per paper — and 6k papers used 20 MB of disk space and took a bit less than 1 hour to index.&lt;/p&gt;
&lt;h2 id="other-tools-worth-checking"&gt;Other tools worth checking &lt;a href="#other-tools-worth-checking" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="mcp-for-various-scientific-engines--search"&gt;MCP for Various scientific engines search &lt;a href="#mcp-for-various-scientific-engines--search" class="hash"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you already installed uv for Zotero MCP, another useful companion tool is paper-find-mcp. Instead of searching your own Zotero library, it lets your AI assistant search across external academic sources such as arXiv, PubMed, bioRxiv, medRxiv, IACR, Google Scholar et cetera.&lt;/p&gt;
&lt;p&gt;It is available on &lt;a href="https://pypi.org/project/paper-find-mcp/"&gt;PyPI&lt;/a&gt; as paper-find-mcp, and the project documents both uv/pip installation and Claude Desktop configuration via an MCP server entry in claude_desktop_config.json.&lt;/p&gt;
&lt;p&gt;If you already installed uv to use with Zotero mcp in previous step, you can easily install this package via:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;uv pip install paper-find-mcp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After installing, you only need to add new mcp defnition to claude config file (the example below), pleasebe careful with correctly closing json brackets.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;open ~/Library/Application Support/Claude/claude_desktop_config.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;mcpServers&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;paper_find_server&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;uvx&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#1f2328"&gt;[&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;paper-find-mcp&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;env&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;SEMANTIC_SCHOLAR_API_KEY&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;CROSSREF_MAILTO&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;your_email@example.com&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;NCBI_API_KEY&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;PAPER_DOWNLOAD_PATH&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;~/paper_downloads&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;preferences&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;chromeExtensionEnabled&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#cf222e"&gt;false&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;coworkScheduledTasksEnabled&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#cf222e"&gt;true&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;sidebarMode&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;chat&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#0550ae"&gt;&amp;#34;coworkWebSearchEnabled&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#cf222e"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description><author>dtborek@gmail.com (Daniel Borek)</author><guid>https://danielborek.me/2026/zotero-mcp-ai/</guid><pubDate>Sun, 08 Feb 2026 00:00:00 +0000</pubDate></item><item><title>Tracking Word Count Progress in Git</title><link>https://danielborek.me/2024/count-progress-git/</link><description>&lt;p&gt;I created a simple script to count word changes in my git repository. I&amp;rsquo;m trying Typst now for writing an article in VSCode, and there&amp;rsquo;s no good extension to quantify daily progress. This script looks at your commits, counts words added, deleted, and duplicated in the files of the extension by your choice (by default .typ and .md files). It shows a daily breakdown of writing progress.&lt;/p&gt;
&lt;p&gt;Got the idea from a &lt;a href="//www.gijsvandam.nl/post/measuring-your-writing-progress-with-a-git-word-count/"&gt;Gijs van Dam&amp;rsquo;s blog post&lt;/a&gt; (who described logic behind the word change calculation). I adapted it by adding Typst and extending the way it display the output. I add some additions like handling the first commit: The script checks if it&amp;rsquo;s dealing with the first commit in the repo, which doesn&amp;rsquo;t have a parent to compare against and it stops.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s been helpful for tracking my word count (and writing it was a nice excuse to not writing the text I supposed to write). Below you can find the gist with the latest version.&lt;/p&gt;
&lt;script src="https://gist.github.com/danieltomasz/e3be94d7f03ade2eb030edf63920e95a.js"&gt;&lt;/script&gt;</description><author>dtborek@gmail.com (Daniel Borek)</author><guid>https://danielborek.me/2024/count-progress-git/</guid><pubDate>Thu, 05 Sep 2024 01:00:00 -0700</pubDate></item><item><title>Github Education Pack and other tips</title><link>https://danielborek.me/2024/git-github-tips/</link><description>&lt;p&gt;GitHub is a powerful platform (if you stayed on it after it was bought by Microsoft) that you can use for version control with Git and collaborative software/project development.&lt;/p&gt;
&lt;p&gt;Apart of its main purpose for managing and maintaining your projects, and many people are familiar with the basic functionalities of GitHub, there are several valuable features that even experienced users might overlook&lt;/p&gt;
&lt;p&gt;This is a bit trivial information but I know that are some people (mostly academic colleagues) who were using Github and for whom this information was new when I shared with them. The post will be updated if I will learn about features.&lt;/p&gt;
&lt;h2 id="if-you-are-a-student-ask-for-the-github-education-pack"&gt;If You Are a Student, Ask for the GitHub Education Pack &lt;a href="#if-you-are-a-student-ask-for-the-github-education-pack" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you are a student, you can apply for the GitHub Student Developer Pack, which offers free access to various developer tools, resources, and services. Some of the best perks include access to Microsoft Copilot inside VSCode, a license for PyCharm Professional Edition (which supports some great features), and a year of a Namecheap domain.&lt;/p&gt;
&lt;p&gt;You also get a year of a free domain with a .me ending on Namecheap, a free 3-month individual subscription to DataCamp, and access to other perks. Some of these perks might not be as useful, but you may find something valuable, like computing credits on Azure and other services for machine learning. More details can be found here: &lt;a href="https://education.github.com/pack"&gt;GitHub Education Pack&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When applying, if you encounter verification issues, you might find useful this comment from related &lt;a href="https://github.com/orgs/community/discussions/61537"&gt;discussion&lt;/a&gt; :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You are unlikely to be verified until you have completed your GitHub user profile with your full name as it appears in your academic affiliation document, plus a short bio.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Overall, having a good presence and short introduction on your profile is a good idea.&lt;/p&gt;
&lt;p&gt;Even after successful verification, you might need to wait up to 72hours, and in peak times like the start of the semester even longer.&lt;/p&gt;
&lt;h2 id="use-favorites-for-easy-access-to-repositories"&gt;Use Favorites for Easy Access to Repositories &lt;a href="#use-favorites-for-easy-access-to-repositories" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The staring a favorites repo is a pretty standard feature, but a new shiny beta feature is “lists,” which you can create and add repositories to when starring them. This is useful for creating low-effort, different collections.&lt;/p&gt;
&lt;h2 id="be-notified-about-releases-of-specific-packages"&gt;Be Notified About Releases of Specific Packages &lt;a href="#be-notified-about-releases-of-specific-packages" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To stay updated on the latest releases of specific packages or repositories, you can “Watch” the repository. This is one of the badges on the top of the repository view. By default, the “Participation and mentions” option is active. When you choose “Custom,” you can select “Releases Only” to stay informed about the latest updates.&lt;/p&gt;
&lt;h2 id="utilize-github-pages-for-hosting"&gt;Utilize GitHub Pages for Hosting &lt;a href="#utilize-github-pages-for-hosting" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;GitHub Pages allows you to host static websites directly from your repositories. This is an excellent way to showcase your projects, create documentation, or even host a personal blog. It’s free and integrates seamlessly with your GitHub workflow.&lt;/p&gt;
&lt;p&gt;For creating webpages you can use static site generators that use markdown.&lt;br&gt;
Hugo and Jekyll are great for that (Hugo is really fast, i switched from Jekyll ), but if you work with code and often want to showcase you project notebooks as blogpost you might think about using Quarto for &lt;a href="https://quarto.org/docs/websites/website-blog.html"&gt;blogging&lt;/a&gt; .&lt;/p&gt;
&lt;h2 id="open-github-repository-on-the-web-in-vscode"&gt;Open Github repository on the web in vscode &lt;a href="#open-github-repository-on-the-web-in-vscode" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can open your repository in the vscode in your browser replacing &lt;code&gt;github.com&lt;/code&gt; by &lt;code&gt;vscode.dev&lt;/code&gt;. It may be useful when you travel and have only access to your tablet et cetera.&lt;/p&gt;</description><author>dtborek@gmail.com (Daniel Borek)</author><guid>https://danielborek.me/2024/git-github-tips/</guid><pubDate>Fri, 14 Jun 2024 01:00:00 -0700</pubDate></item><item><title>Streamline Your Research: Zotero's File Management Tricks</title><link>https://danielborek.me/2024/zotero-rename-files/</link><description>&lt;p&gt;After listening an episode of &lt;code&gt;PhD Life Coach&lt;/code&gt; podcast (recommend to everyone related to academia) on &lt;a href="https://thephdlifecoach.buzzsprout.com/1992545/15220518-2-39-how-to-be-your-own-best-personal-assistant"&gt;How to be your own best personal assistant&lt;/a&gt; Iearned that some academic people still spent their time on renaming pdfs downloaded from internet. I want to share an important message to those people :) :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If you deal if structured articles (web, pdf) please use Reference Mangers, even if you don&amp;rsquo;t cite those articles that often; It will remove many of your headaches and you can have nice organization of your research materials&lt;/li&gt;
&lt;li&gt;Even if you want to store PDFs inside specific projects a Reference Mangers can be really useful by automatic file renaming for you (you can move this files to another folder later). If you need to standardize the naming convention for all your PDF files or want to include specific metadata in the file names, Zotero or other reference mangers offers built-in functionalities and add-ons to achieve this efficiently.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here a quick tips how to rename files using Zotero.&lt;/p&gt;
&lt;p&gt;I am using &lt;a href="https://www.zotero.org/support/beta_builds"&gt;Zotero 7 Beta&lt;/a&gt; (which offer nicer interface that &lt;a href="https://www.zotero.org/download/"&gt;Zotero 6&lt;/a&gt; and some additional features like epub or webpages annotation, and is really stable and I hope will soon released as stable version) so my description might be slightly different from what people using previous version may sees .&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In Zotero go to &lt;code&gt;Edit&lt;/code&gt; &amp;gt; &lt;code&gt;Preferences&lt;/code&gt; (or &lt;code&gt;Zotero&lt;/code&gt; &amp;gt; &lt;code&gt;Preferences&lt;/code&gt; on a Mac).&lt;/li&gt;
&lt;li&gt;In the General tab, check the box •&lt;code&gt;Automatically rename locally added files&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &amp;lsquo;Rename linked files&amp;rsquo; will matter only the files added to Zotero items aren&amp;rsquo;t story with Zotero folder (where every bibliographic item has its own folder with the not so meaningful alphanumeric name ) but out outside of Zotero (when you move the file, you will broke the link )&lt;/p&gt;
&lt;p&gt;if you click &lt;code&gt;Customize Filename Format…&lt;/code&gt; you can customize the format,.&lt;/p&gt;
&lt;p&gt;For example using filename template&lt;br&gt;
&lt;code&gt;{{ firstCreator suffix=&amp;quot; - &amp;quot; }}{{ year suffix=&amp;quot; - &amp;quot; }}{{ title truncate=&amp;quot;100&amp;quot; }}&lt;/code&gt; the resulting name will result in the filename similar to the one below:
&lt;code&gt;Aljalal et al. - 2024 - Selecting EEG channels and features using multi-objective optimization for accurate MCI detection v.pdf&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;On the official documentation page you can find the options you can use explained in more depth &lt;a href="https://www.zotero.org/support/file_renaming"&gt;file_renaming [Zotero Documentation]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You easily rename in bulk the files that you already downloaded and store in Zotero if for some reason you prefer to change the pattern in the future.&lt;/p&gt;
&lt;p&gt;Zotero is adding a great QoL value with its Browser Plugin Zotero Connector (which automatically download and add pdf (if you have access to it on publishers page) or snapshot of webpage to your local library ), and apparently this is not a comment knowledge, as prof. Inga Mewburn from [The Thesis Whisperer][thttps://thesiswhisperer.com/] blog and On the reg podcast recently admitted in one of the episodes that she learned about the connector after few years of using Zotero.&lt;/p&gt;
&lt;p&gt;If you prefer to have linked files in one big folder, the Zotfile or other plugin that offer the same capabilities are something for you.&lt;/p&gt;
&lt;p&gt;Even if you prefer have PDFs in the folders of your projects, you can always easily open folder containing the renamed files and move it to the project folder.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t create here a full tutorial or covered many helpful features of Zotero, but you can find many great tutorials on the internet. I am also a big fan of integrating Zotero with &lt;a href="https://obsidian.md/"&gt;Obsidian&lt;/a&gt;, you could find some example vault how it could be used here:&lt;a href="https://github.com/rlaker/Obsidian-for-Academia"&gt;rlaker/Obsidian-for-Academia: Demo vault to explain how to use Obsidian, git and Zotero&lt;/a&gt; with another writeup here: &lt;a href="https://radoncnotes.com/2024/03/08/integrating-obsidian-with-zotero/"&gt;Integrating Obsidian with Zotero – RadOncNotes&lt;/a&gt;.&lt;/p&gt;</description><author>dtborek@gmail.com (Daniel Borek)</author><guid>https://danielborek.me/2024/zotero-rename-files/</guid><pubDate>Thu, 13 Jun 2024 00:00:00 +0000</pubDate></item><item><title>Zotero 7 is coming …</title><link>https://danielborek.me/2023/zotero-7/</link><description>&lt;p&gt;Edit 1: Zotero 7 was realeased officialy in August 2024.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.zotero.org/"&gt;Zotero&lt;/a&gt; is a free, open-source reference management software. It make the process of collecting and storing references pretty easy (sometimes too easy !).&lt;/p&gt;
&lt;p&gt;The latest stable, big release version, Zotero 6, was introduced in March 2022. It brought a new built-in PDF viewer and annotation system, as well as iOS and iPadOS apps.&lt;/p&gt;
&lt;h2 id="zotero-7-is-coming"&gt;Zotero 7 is Coming &lt;a href="#zotero-7-is-coming" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Recently, the devs shared a public beta of Zotero 7. Most of the updates are related to transitioning from the legacy Firefox 60 ESR, which Zotero was originally built on top of (Zotero started as a browser extension), to the newer Firefox 102 ESR now and Firefox 115 ESR before the release this year.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s exciting is the native support for Apple Silicon desktop devices and an &lt;a href="https://forums.zotero.org/discussion/106716/available-for-beta-testing-updated-reader-with-epub-snapshot-support-and-new-annotation-types"&gt;even better reader, with EPUB support and web annotation&lt;/a&gt;. An Android app is also in development.&lt;/p&gt;
&lt;!--Markdow export
zotero://open-pdf/library/items/9WYPFUSR?page=9
https://forums.zotero.org/discussion/90858/pdf-reader-and-zotero-open-pdf-links
--&gt;
&lt;h2 id="plugins-im-using-in-zotero-7"&gt;Plugins I&amp;rsquo;m Using in Zotero 7 &lt;a href="#plugins-im-using-in-zotero-7" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As an avid Zotero user, I&amp;rsquo;ve tested my fair share of plugins to optimize my workflow. While Zotero is great on its own, plugin take it to the next level for managing citations and references. Unfortunately with the changes to Zotero app, many plugins doesnt support beta yet. Below those I am using and are ported (or will be ported soon):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/retorquere/zotero-open-pdf"&gt;GitHub - retorquere/zotero-open-pdf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Since introduction of Zotero 6 users can opt to use new build-in PDF reader. I like the new new reader, but sometimes need to open the PDF in external PDF reader (PDF Expert in my case). This simple plugin lets you open PDF in external PDF reader via right click menu, instead of tedious opening in folder.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://retorque.re/zotero-better-bibtex/"&gt;Better BibTeX for Zotero :: Better BibTeX for Zotero&lt;/a&gt;.A must-have for LaTeX/Obsidian/Pandoc users like myself. Provide citekeys. Ported with much of work due to many changes in Zotero 7.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/windingwind/zotero-pdf-preview"&gt;GitHub - windingwind/zotero-pdf-preview: PDF Preview for Zotero.&lt;/a&gt; Enables live PDF miniature previews right in my Zotero item info pane so I can quickly confirm I have looking into right paper without open the item. Current changes to PDF Reader (Zotero 7 beta 29) broke it, it still doesn&amp;rsquo;t work as for the middle of August;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/wshanks/Zutilo"&gt;GitHub - wshanks/Zutilo: Zotero plugin providing some additional editing features&lt;/a&gt; A nice collection of tools, still in the process of porting.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While Zotero 7 has some additional compatible plugins I don&amp;rsquo;t use or haven&amp;rsquo;t tested, many of the best plugins remain available only for Zotero 6.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Edit 2:&lt;/em&gt; Some additional extensions not mentioned earlier, that I am currently using (in 2024):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/justinribeiro/zotero-google-scholar-citation-count"&gt;justinribeiro/zotero-google-scholar-citation-count: Zotero plugin for fetching number of citations from Google Scholar.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/daeh/zotero-markdb-connect#readme"&gt;daeh/zotero-markdb-connect: Zotero plugin that links your Markdown database to Zotero. Jump directly from Zotero Items to connected Markdown files. Automatically tags Zotero Items so you can easily see which papers you&amp;rsquo;ve made notes for.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chenglongma.com/zoplicate/"&gt;Zoplicate | A plugin that does one thing only - Detect and manage duplicate items in Zotero.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/github-young/zotero-better-authors#readme"&gt;github-young/zotero-better-authors: Customize the displayed authors list&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="markdown-translators"&gt;Markdown translators &lt;a href="#markdown-translators" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Useful pair of Zotero translators when you want to link to Zotero item as Markdown link&lt;br&gt;
&lt;a href="https://github.com/erazlogo/zotero-markdown-translators"&gt;https://github.com/erazlogo/zotero-markdown-translators&lt;/a&gt;&lt;/p&gt;</description><author>dtborek@gmail.com (Daniel Borek)</author><guid>https://danielborek.me/2023/zotero-7/</guid><pubDate>Tue, 15 Aug 2023 00:00:00 +0000</pubDate></item><item><title>PDF tweaks and tips with Quarto</title><link>https://danielborek.me/2023/quarto-tips/</link><description>&lt;p&gt;&lt;em&gt;Disclaimer: This is work in progress, I will be updating this post as I standardize and create a separate template/extension repository.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I wrote my master thesis in LaTeX, but given that markdown is a bit nicer to write, so I decided to switch with my PhD thesis to Markdown.&lt;br&gt;
I was trying with Pandoc but last year I switched to Quarto, as it is more flexible, and it is easier to customize.&lt;/p&gt;
&lt;h1 id="nicer-fonts-and-better-handling-of-figures"&gt;Nicer Fonts and Better Handling of Figures &lt;a href="#nicer-fonts-and-better-handling-of-figures" class="hash"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I am sharing with you a fragment of my &lt;code&gt;_quarto.yml&lt;/code&gt; file, with some tweaks I am using to make my PDF output nicer.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;format&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;pdf&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;link-citations&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#cf222e"&gt;true&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#57606a"&gt;# &amp;lt;1&amp;gt;&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;number-sections&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#cf222e"&gt;false&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;reference-section-title&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;References&amp;#34;&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;pdf-engine&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;xelatex&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;include-in-header&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;text&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#1f2328"&gt;|&lt;/span&gt;&lt;span style="color:#0a3069"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; \usepackage{epigraph}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; %%
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; %% Code related to fonts and how the output looks
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; %%
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; \usepackage{mathpazo} % &amp;lt;2&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; \usepackage[T1]{fontenc}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; \usepackage[sups,osf]{fbb} % osf (or tosf) for text, not math # &amp;lt;2&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; \usepackage[scaled=.95]{cabin} % sans serif # &amp;lt;2&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; \usepackage[varqu,varl]{inconsolata} % sans serif typewriter # &amp;lt;2&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; %%
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; %% Code related to figures in document
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; %%
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; \usepackage{float} % &amp;lt;3&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; \graphicspath{{figures}{chapters/figures}{../figures}{chapters}} # &amp;lt;4&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; \let\origfigure\figure
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; \let\endorigfigure\endfigure
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; \renewenvironment{figure}[1][2] {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; \expandafter\origfigure\expandafter[H]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; } {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; \endorigfigure
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; }&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I put number as comments, so you can better understand specific lines.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Some options for PDF output (more or less self-explanatory)&amp;gt;&lt;/li&gt;
&lt;li&gt;The font use by default template are ugly format, this look similar but are nicer.&lt;/li&gt;
&lt;li&gt;Forcing figures to be placed where they are in the text&lt;/li&gt;
&lt;li&gt;Allowing to use figures from different folders, and allowing to use relative paths to figures.&lt;/li&gt;
&lt;/ol&gt;
&lt;!--Those are setting I am using to render a working preview of a chapter.
The final version will be using a font provided by my university: --&gt;
&lt;h1 id="use-profiles-to-compile-either-whole-book-or-thesis-or-single-chapter"&gt;Use Profiles to Compile either Whole Book or Thesis or Single Chapter &lt;a href="#use-profiles-to-compile-either-whole-book-or-thesis-or-single-chapter" class="hash"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Since quarto 1.2 profiles are introduced &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; In my use case I have on profile &lt;code&gt;_quarto-thesis.yml&lt;/code&gt; which include all chapters and preambles, and another &lt;code&gt;_quarto.yml&lt;/code&gt; which I can use just to compile the current markdown file; If I want to render whole thesis I just use &lt;code&gt;quarto render --profile thesis&lt;/code&gt;; you could have as many as you want of those profiles (I was procrastinating that way with different look and templates for my thesis :slight_smile: )&lt;/p&gt;
&lt;h1 id="roman-numbering-for-front-matter-and-arabic-for-main-matter"&gt;Roman Numbering for front Matter and Arabic for Main Matter &lt;a href="#roman-numbering-for-front-matter-and-arabic-for-main-matter" class="hash"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I am inserting roman numbering for front matter and arabic for main matter.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;format&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;pdf&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;include-before-body&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;text&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#1f2328"&gt;|&lt;/span&gt;&lt;span style="color:#0a3069"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; \pagestyle{plain}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0a3069"&gt; \pagenumbering{roman}&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In main tex I switch back to arabic numbering:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-tex" data-lang="tex"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;\pagenumbering&lt;/span&gt;&lt;span style="color:#6639ba"&gt;{&lt;/span&gt;arabic&lt;span style="color:#6639ba"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="problem-with-indexmd-and-inserted-blank-page"&gt;Problem with &lt;em&gt;index.md&lt;/em&gt; and Inserted Blank Page &lt;a href="#problem-with-indexmd-and-inserted-blank-page" class="hash"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;As you write complex project, you want might want to have separate files for each chapter.&lt;br&gt;
When you are using &lt;code&gt;quarto&lt;/code&gt; to render your project, you could put them inside &lt;code&gt;index.md&lt;/code&gt; in your root folder:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{{include &amp;#34;chapters/chapter1.qmd&amp;#34;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{{include &amp;#34;chapters/chapter2.qmd&amp;#34;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using &lt;code&gt;include&lt;/code&gt; filter will add the content of the files to the &lt;code&gt;index.md&lt;/code&gt; file when compiling.&lt;br&gt;
This is nice solution, but I have a problem when I want to use &lt;code&gt;quarto preview&lt;/code&gt; to update and monitor resulting pdf when I change something in source files.&lt;br&gt;
Unfortunately, &lt;code&gt;quarto preview&lt;/code&gt; does not update output &lt;code&gt;.pdf&lt;/code&gt; file when files added with &lt;code&gt;include&lt;/code&gt; change, so I needed to choose another solution.&lt;/p&gt;
&lt;p&gt;Alternative solution is to add chapters into you &lt;code&gt;_quarto.yml&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;project&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;output-dir&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;_output&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;execute-dir&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;file&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;book&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;title&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;Title of my thesis or book&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;author&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;name&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;Author Name&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;chapters&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;- index.qmd&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;- chapters/chapter1.md&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;- chapters/chapter1.md&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I want to all of my content of to live in the &amp;lsquo;chapters&amp;rsquo; folder.&lt;br&gt;
Unfortunately currently in &lt;code&gt;quatro&lt;/code&gt; users always need to include &lt;code&gt;index.md&lt;/code&gt; (it makes sense when generating &lt;code&gt;html&lt;/code&gt; as output, but not always when generating &lt;code&gt;pdf&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;I can leave the &lt;code&gt;index.md&lt;/code&gt; file empty, but then I will have an empty page between my table of content and first chapter.&lt;br&gt;
I can include the first chapter into index.md but then we were hitting the same problem on smaller scale again.&lt;/p&gt;
&lt;h2 id="my-solution"&gt;My Solution &lt;a href="#my-solution" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By default, latex is adding a page break after each chapter, and the &lt;code&gt;index.md&lt;/code&gt; file is treated as a chapter, even if empty.&lt;br&gt;
As a hack, I am hiding the problem by relaxing &lt;code&gt;\clearpage&lt;/code&gt; behaviour in latex.&lt;/p&gt;
&lt;p&gt;I am leaving my &lt;code&gt;index.md&lt;/code&gt; file almost empty, I&amp;rsquo;m just adding this latex code &lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-tex" data-lang="tex"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;\let\standardclearpage\clearpage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;\let\clearpage\relax&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then within my first chapter when my content start, I am returning to standard behaviour of &lt;code&gt;\clearpage&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-tex" data-lang="tex"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;\let\clearpage\standardclearpage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This way I am not adding any extra page between my table of content and first chapter, and I can use &lt;code&gt;quarto preview&lt;/code&gt; to monitor my changes.&lt;br&gt;
In future the &lt;code&gt;quatro&lt;/code&gt; behaviour might change regarding inclusion of `index.md``, you can follow discussion here:&lt;/p&gt;
&lt;h1 id="useful-materials"&gt;Useful Materials &lt;a href="#useful-materials" class="hash"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I just saw this post published on the blog of Cameron Patrick, it contains more useful information about writing thesis with &lt;code&gt;quarto&lt;/code&gt;: &lt;a href="https://cameronpatrick.com/post/2023/07/quarto-thesis-formatting/."&gt;https://cameronpatrick.com/post/2023/07/quarto-thesis-formatting/.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I am personally using modified &lt;code&gt;quarto-thesis&lt;/code&gt; template for my thesis, you can find it here: &lt;a href="https://github.com/nmfs-opensci/quarto-thesis"&gt;https://github.com/nmfs-opensci/quarto-thesis&lt;/a&gt;&lt;br&gt;
If you don&amp;rsquo;t use quarto, but you want to play with it, you can modify it online on the Posit virtual &lt;code&gt;Rstudio&lt;/code&gt; instance &lt;a href="https://rstudio.cloud/content/4383755"&gt;https://rstudio.cloud/content/4383755&lt;/a&gt; (free account required)&lt;/p&gt;
&lt;h1 id="references"&gt;References &lt;a href="#references" class="hash"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://quarto.org/docs/projects/profiles.html"&gt;Quarto - Project Profiles&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;a href="https://tex.stackexchange.com/a/176109"&gt;https://tex.stackexchange.com/a/176109&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><author>dtborek@gmail.com (Daniel Borek)</author><guid>https://danielborek.me/2023/quarto-tips/</guid><pubDate>Fri, 04 Aug 2023 00:00:00 +0000</pubDate></item><item><title>From Quarto with love (but back to Hugo)</title><link>https://danielborek.me/2023/back-to-hugo/</link><description>&lt;p&gt;I&amp;rsquo;ve been using different static site generators over the years - Jekyll, Hugo, and most recently Quarto. In this post I&amp;rsquo;ll share my experiences and how I&amp;rsquo;m now using Hugo and Quarto together for my blog.&lt;/p&gt;
&lt;h1 id="why-i-switched-back-to-hugo"&gt;Why I Switched back to Hugo &lt;a href="#why-i-switched-back-to-hugo" class="hash"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I used Jekyll years ago (you can see one of the pages I created &lt;a href="https://brainhackwarsaw2017.github.io/"&gt;here&lt;/a&gt;), then switched to Hugo, and about a year ago I started using Quarto.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quarto&lt;/strong&gt; is great for scientific publishing, but I found it too limiting for a personal blog:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every post must be in its own folder with an &lt;code&gt;index.qmd&lt;/code&gt; file. This works well for showcasing content, but isn&amp;rsquo;t convenient for writing.&lt;/li&gt;
&lt;li&gt;As a scientific publishing tool, the blog features remain an afterthought. The Quarto team is great about adding features, but blog improvements happen slowly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I decided to switch back to &lt;strong&gt;Hugo&lt;/strong&gt; as my static site generator for the flexibility.&lt;/p&gt;
&lt;p&gt;Fortunately, there&amp;rsquo;s no need to fully abandon Quarto. &lt;a href="https://quarto.org/docs/output-formats/hugo.html"&gt;Quarto has built-in support for outputting to Hugo&lt;/a&gt;. I can use Quarto to render my posts written in &lt;code&gt;qmd&lt;/code&gt; format into Markdown that Hugo can use.&lt;/p&gt;
&lt;h1 id="quarto-loves-hugo"&gt;Quarto Loves Hugo &lt;a href="#quarto-loves-hugo" class="hash"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Here is a quick overview of how I have Hugo and Quarto working together:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I added some config settings to my Hugo &lt;code&gt;config.toml&lt;/code&gt; file:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;ignoreFiles&lt;/span&gt; &lt;span style="color:#1f2328"&gt;=&lt;/span&gt; &lt;span style="color:#1f2328"&gt;[&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;\\.qmd$&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;\\.ipynb$&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;\\.py$&amp;#34;&lt;/span&gt; &lt;span style="color:#1f2328"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;[&lt;/span&gt;&lt;span style="color:#1f2328"&gt;markup&lt;/span&gt;&lt;span style="color:#1f2328"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;defaultMarkdownHandler&lt;/span&gt; &lt;span style="color:#1f2328"&gt;=&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;goldmark&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;[&lt;/span&gt;&lt;span style="color:#1f2328"&gt;markup&lt;/span&gt;&lt;span style="color:#1f2328"&gt;.&lt;/span&gt;&lt;span style="color:#1f2328"&gt;goldmark&lt;/span&gt;&lt;span style="color:#1f2328"&gt;.&lt;/span&gt;&lt;span style="color:#1f2328"&gt;renderer&lt;/span&gt;&lt;span style="color:#1f2328"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;unsafe&lt;/span&gt; &lt;span style="color:#1f2328"&gt;=&lt;/span&gt; &lt;span style="color:#cf222e"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I have a &lt;code&gt;content/posts&lt;/code&gt; folder containing subfolders for each post, with a &lt;code&gt;index.qmd&lt;/code&gt; file inside.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I put this &lt;code&gt;_quarto.yml&lt;/code&gt; in the Hugo root directory:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;project&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;type&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;hugo&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;format&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;hugo-md&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;code-fold&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#cf222e"&gt;true&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;html-math-method&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;webtex&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;execute&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#0550ae"&gt;warning&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#cf222e"&gt;false&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;To create post written in &lt;code&gt;qmd&lt;/code&gt; files, I run &lt;code&gt;quarto render&lt;/code&gt; which outputs Markdown from my &lt;code&gt;qmd&lt;/code&gt; posts into the Hugo content folder.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also found two nice blog posts from people using Quarto with Hugo:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cedricbatailler.me/blog/2022-apero/"&gt;🪄 Quarto, Hugo, Apero | Cédric Batailler&lt;/a&gt;. Cédric is using lovely Apero theme. I am finding this theme great, but I choose to stick with minimalism of lines theme.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.paulrjohnson.net/2022/09/setting-up-a-hugo-website-with-quarto/"&gt;Setting up a Hugo Website with Quarto | Paul Johnson&lt;/a&gt;. Paul is using a PaperMod theme, which is also very nice. He is a bit more technical in his post&lt;/li&gt;
&lt;li&gt;Paul Johnson describe how he render mermaid diagrams (supported in Quarto) in &lt;a href="https://www.paulrjohnson.net/2022/09/rendering-mermaid-diagrams-on-a-hugo-website-using-quarto/"&gt;this blogpost&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="customizing-the-theme"&gt;Customizing the Theme &lt;a href="#customizing-the-theme" class="hash"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I started from the lines theme, you can find it &lt;a href="https://github.com/ronv/lines"&gt;here&lt;/a&gt;.&lt;br&gt;
Later I mixed it with another theme, &lt;a href="https://github.com/ronv/simplist"&gt;simplist&lt;/a&gt; by the same author and I added my custom CSS parts.&lt;/p&gt;
&lt;h1 id="more-customisation"&gt;More Customisation &lt;a href="#more-customisation" class="hash"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I started with the Lines theme, customised it with styles from the Simplist theme, and added some of my own CSS.&lt;/p&gt;
&lt;h2 id="hugo-and-github-pages"&gt;Hugo and GitHub Pages &lt;a href="#hugo-and-github-pages" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I followed &lt;a href="https://gohugo.io/hosting-and-deployment/hosting-on-github/"&gt;Hugo&amp;rsquo;s GitHub Pages guide&lt;/a&gt; but ran into some issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The theme submodule wasn&amp;rsquo;t syncing properly. I had to manually add the &lt;code&gt;.gitmodules&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;The theme&amp;rsquo;s example content was cluttering my published site. I forked the theme and removed the examples.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is my final &lt;code&gt;.gitmodules&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;[&lt;/span&gt;&lt;span style="color:#1f2328"&gt;submodule&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;themes/lines&amp;#34;&lt;/span&gt;&lt;span style="color:#1f2328"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;path&lt;/span&gt; &lt;span style="color:#1f2328"&gt;=&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;themes/lines&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#1f2328"&gt;url&lt;/span&gt; &lt;span style="color:#1f2328"&gt;=&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;https://github.com/danieltomasz/lines.git&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="hugo-and-code-highlighting"&gt;Hugo and Code Highlighting &lt;a href="#hugo-and-code-highlighting" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To get code highlighting in backticks markdown fences I enabled:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;pygmentsStyle&lt;/span&gt; &lt;span style="color:#1f2328"&gt;=&lt;/span&gt; &lt;span style="color:#0a3069"&gt;&amp;#34;pygments&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;pygmentsCodefences&lt;/span&gt; &lt;span style="color:#1f2328"&gt;=&lt;/span&gt; &lt;span style="color:#cf222e"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="adding-comments-with-giscus"&gt;Adding Comments with &lt;code&gt;giscus&lt;/code&gt; &lt;a href="#adding-comments-with-giscus" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I followed various blogpost with the configuration advices.&lt;br&gt;
By default, &lt;code&gt;giscus&lt;/code&gt; is adding comments to every page, but I wanted to have comments only on my posts.&lt;br&gt;
I followed advice&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; and I wrapped my addition of &lt;code&gt;giscus&lt;/code&gt; partial into &lt;code&gt;footer.html&lt;/code&gt; with &lt;code&gt;if&lt;/code&gt; statement:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-hugo" data-lang="hugo"&gt;{{ if not .Params.noComment }}
{{ partial &amp;#34;giscus&amp;#34; . }}
{{ end }}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When I don&amp;rsquo;t want to include a comments block, I am adding this in the frontmatter of the post (yaml):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;noComment&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt; &lt;/span&gt;&lt;span style="color:#cf222e"&gt;true&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Some other useful links: &lt;a href="https://www.brycewray.com/posts/2022/05/tips-using-giscus/"&gt;https://www.brycewray.com/posts/2022/05/tips-using-giscus/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="date-of-last-edit-directly-from-github"&gt;Date of Last Edit Directly from Github &lt;a href="#date-of-last-edit-directly-from-github" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some advice how to set up it: &lt;a href="https://makewithhugo.com/add-a-last-edited-date/"&gt;Add a Last Edited Date to Posts · Make with Hugo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One caveat: setting &lt;code&gt;enableGitInfo&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; in your site’s configuration file sometimes is not enough&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;. You have to add &lt;code&gt;--enableGitInfo&lt;/code&gt; to get &lt;code&gt;.GitInfo&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hugo serve --enableGitInfo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://discourse.gohugo.io/t/how-to-disable-comments-in-specific-pages/22177/2"&gt;How to disable comments in specific pages? - support - HUGO&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;a href="https://djangocas.dev/blog/add-git-commit-date-as-last-update-date-in-hugo-page/"&gt;Add git commit date as last update date in hugo page&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><author>dtborek@gmail.com (Daniel Borek)</author><guid>https://danielborek.me/2023/back-to-hugo/</guid><pubDate>Thu, 03 Aug 2023 00:00:00 +0000</pubDate></item><item><title>Using Wikilinks and Git/Obsidian Callouts in Quarto Markdown</title><link>https://danielborek.me/2023/obsidian-quarto-callouts/</link><description>&lt;h1 id="about-callouts"&gt;About Callouts &lt;a href="#about-callouts" class="hash"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: Previous version of this post was originally published as a &lt;a href="https://gist.github.com/danieltomasz/87b1321e23c045309d2571f525f856cf"&gt;public gist&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Callouts are a great way to highlight important details in your text by adding boxes with icons. Many flavours of markdown support them (but might use different syntax).&lt;/p&gt;
&lt;p&gt;Quarto supports 5 callout types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;note&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;warning&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;important&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tip&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;caution&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each type has a different color and icon. You can see examples in the &lt;a href="https://quarto.org/docs/authoring/callouts.html"&gt;Quarto documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem &lt;a href="#the-problem" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To use callouts in Quarto markdown (&lt;code&gt;qmd&lt;/code&gt;) files, you need to use Pandoc&amp;rsquo;s div syntax:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-md" data-lang="md"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;::: {.callout-note}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;This is a note callout
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;:::
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But Obsidian doesn&amp;rsquo;t recognise this syntax. Instead, it uses:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;&amp;gt; &lt;/span&gt;&lt;span style="color:#1f2328"&gt;[!note] My note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;&amp;gt; Note content
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So there&amp;rsquo;s no easy way to preview nice callouts in Obsidian before compiling to PDF.&lt;/p&gt;
&lt;p&gt;Github also introduced callouts into its markdown flavour with the same syntax under name of &lt;a href="https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts"&gt;Alerts&lt;/a&gt;&lt;/p&gt;
&lt;h1 id="lua-filters-to-the-rescue"&gt;Lua Filters to the Rescue &lt;a href="#lua-filters-to-the-rescue" class="hash"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Pandoc supports Lua filters that can modify the markdown before compilation.&lt;/p&gt;
&lt;p&gt;I found a filter on the Obsidian forum and modified it to support Quarto callouts. You need to save the filter as &lt;code&gt;obsidian-callouts.lua&lt;/code&gt; in your project&amp;rsquo;s &lt;code&gt;filters&lt;/code&gt; folder.&lt;/p&gt;
&lt;p&gt;Then point to it in &lt;code&gt;_quarto.yml&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;filters&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- filters/obsidian-callouts.lua&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Content of the file &lt;code&gt;obsidian-callouts.lua&lt;/code&gt; is in the following gist :&lt;/p&gt;
&lt;script src="https://gist.github.com/danieltomasz/31d298aca2969adaf60d8841b68005e2.js"&gt;&lt;/script&gt;
&lt;p&gt;This makes Obsidian&amp;rsquo;s callout syntax compile properly in Quarto.&lt;/p&gt;
&lt;h1 id="tweaking-obsidian-css"&gt;Tweaking Obsidian CSS &lt;a href="#tweaking-obsidian-css" class="hash"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;By default Obsidian only styles &lt;code&gt;note&lt;/code&gt; and &lt;code&gt;warning&lt;/code&gt; callouts. To match Quarto, add this CSS:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#57606a"&gt;/* See https://lucide.dev for icon codes */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#57606a"&gt;/* annotation */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;.&lt;/span&gt;&lt;span style="color:#1f2328"&gt;callout&lt;/span&gt;&lt;span style="color:#0550ae"&gt;[&lt;/span&gt;&lt;span style="color:#0550ae"&gt;data-callout&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;important&amp;#34;&lt;/span&gt;&lt;span style="color:#0550ae"&gt;]&lt;/span&gt; &lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#953800"&gt;--callout-color&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#0550ae"&gt;251&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt; &lt;span style="color:#0550ae"&gt;70&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt; &lt;span style="color:#0550ae"&gt;76&lt;/span&gt;&lt;span style="color:#1f2328"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#953800"&gt;--callout-icon&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; lucide-alert-circle
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;.&lt;/span&gt;&lt;span style="color:#1f2328"&gt;callout&lt;/span&gt;&lt;span style="color:#0550ae"&gt;[&lt;/span&gt;&lt;span style="color:#0550ae"&gt;data-callout&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;tip&amp;#34;&lt;/span&gt;&lt;span style="color:#0550ae"&gt;]&lt;/span&gt; &lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#953800"&gt;--callout-color&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#0550ae"&gt;28&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt; &lt;span style="color:#0550ae"&gt;207&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt; &lt;span style="color:#0550ae"&gt;110&lt;/span&gt;&lt;span style="color:#1f2328"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#953800"&gt;--callout-icon&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; lucide-lightbulb
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;.&lt;/span&gt;&lt;span style="color:#1f2328"&gt;callout&lt;/span&gt;&lt;span style="color:#0550ae"&gt;[&lt;/span&gt;&lt;span style="color:#0550ae"&gt;data-callout&lt;/span&gt;&lt;span style="color:#0550ae"&gt;=&lt;/span&gt;&lt;span style="color:#0a3069"&gt;&amp;#34;caution&amp;#34;&lt;/span&gt;&lt;span style="color:#0550ae"&gt;]&lt;/span&gt; &lt;span style="color:#1f2328"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#953800"&gt;--callout-color&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; &lt;span style="color:#0550ae"&gt;255&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt;&lt;span style="color:#0550ae"&gt;153&lt;/span&gt;&lt;span style="color:#1f2328"&gt;,&lt;/span&gt;&lt;span style="color:#0550ae"&gt;102&lt;/span&gt;&lt;span style="color:#1f2328"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#953800"&gt;--callout-icon&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt; lucide-flame
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the callouts look (almost) the same in Obsidian and compiled PDFs!&lt;/p&gt;
&lt;p&gt;Below is the Obsidian markdown that can be used to generate the basic 5 types of callouts that map nicely to Quarto types:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;&amp;gt; &lt;/span&gt;&lt;span style="color:#1f2328"&gt;[!note] My note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;&amp;gt; Note content
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;&amp;gt; &lt;/span&gt;&lt;span style="color:#1f2328"&gt;[!warning] My note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;&amp;gt; Note content
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;&amp;gt; &lt;/span&gt;&lt;span style="color:#1f2328"&gt;[!important] My note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;&amp;gt; Note content
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;&amp;gt; &lt;/span&gt;&lt;span style="color:#1f2328"&gt;[!tip] My note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;&amp;gt; Note content
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;&amp;gt; &lt;/span&gt;&lt;span style="color:#1f2328"&gt;[!caution] My note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cf222e"&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#1f2328"&gt;&amp;gt; Note content
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Obsidian support for callouts is much more &lt;a href="https://help.obsidian.md/Editing+and+formatting/Callouts"&gt;extensive&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="caveat"&gt;Caveat &lt;a href="#caveat" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This approach makes callouts render nicely in Obsidian, but may break other Quarto-supporting editors. Test thoroughly before relying on it.&lt;/p&gt;
&lt;p&gt;Remember that in order for callouts to work you need to leave an empty line starting with &lt;code&gt;&amp;gt;&lt;/code&gt; between title and content. If you want a line break in the rendered callout, you might need the same trick.&lt;/p&gt;
&lt;p&gt;Since Quarto 1.3 callouts are represented as a custom AST node. &lt;a href="https://forum.obsidian.md/t/rendering-callouts-similarly-in-pandoc/40020/6"&gt;An earlier version of Lua filter that generated native Pandoc Divs&lt;/a&gt; will not work with latest Quarto.&lt;/p&gt;
&lt;h2 id="bonus-wikilinks"&gt;Bonus: Wikilinks &lt;a href="#bonus-wikilinks" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Obsidian also supports wikilinks, which are a great way to link between notes. They are not supported in Quarto, but you can use a Lua filter to convert them to regular text in the rendered file. Unfortunately, this version doesn&amp;rsquo;t fully support aliases in wikilinks. Also be careful, as this gist is not thouroughly tested, but works in all simple cases I tested it.&lt;/p&gt;
&lt;script src="https://gist.github.com/danieltomasz/bde887dcc775bd47acc0ddf993e3ec2b.js"&gt;&lt;/script&gt;
&lt;p&gt;Add it to &lt;code&gt;_quarto.yml&lt;/code&gt; or the yaml header of your document:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#0550ae"&gt;filters&lt;/span&gt;&lt;span style="color:#1f2328"&gt;:&lt;/span&gt;&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- filters/wikilinks-filter.lua&lt;span style="color:#fff"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="more-obsidian--quarto-tips"&gt;More Obsidian + Quarto Tips &lt;a href="#more-obsidian--quarto-tips" class="hash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I use plugins to preview Quarto files in Obsidian:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;obsidian-shellcommands&lt;/code&gt; to run &lt;code&gt;quarto render&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Custom button with &lt;code&gt;commander&lt;/code&gt; to rerun compilation&lt;/li&gt;
&lt;li&gt;&lt;code&gt;qmd-as-md-obsidian&lt;/code&gt; for basic &lt;code&gt;qmd&lt;/code&gt; support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See the &lt;a href="https://github.com/danieltomasz/qmd-as-md-obsidian"&gt;plugin README&lt;/a&gt; for more workflow advice.&lt;/p&gt;
&lt;p&gt;Let me know if you have any other questions!&lt;/p&gt;</description><author>dtborek@gmail.com (Daniel Borek)</author><guid>https://danielborek.me/2023/obsidian-quarto-callouts/</guid><pubDate>Wed, 31 May 2023 00:00:00 +0000</pubDate></item></channel></rss>