manbytesgnu_site

Source files for manbytesgnu.org
git clone git://holbrook.no/manbytesgnu_site.git
Info | Log | Files | Refs

20240517_localmd.rst (9882B)


      1 Support Your Local Viewer
      2 #########################
      3 
      4 :date: 2024-07-07 21:03:40
      5 :updated: 2024-07-21 01:53:05
      6 :category: Offlining
      7 :author: Louis Holbrook
      8 :tags: bash,markdown,pandoc,vimb,w3m,lynx,xdg
      9 :slug: local-markdown
     10 :summary: Bash script to render and spawn a viewer for markdown files
     11 :lang: en
     12 :status: published
     13 
     14 
     15 Markdown is the fast-food of document formats.
     16 
     17 That doesn't change the fact that it's everywhere.
     18 
     19 So much everywhere, in fact, that it's kind of puzzling there is not a dedicated tool around to view it.
     20 
     21 
     22 Pinning down markdown
     23 =====================
     24 
     25 There is no shortage of applications that *can* render markdown. Among the alternatives are free code editors like Atom_ or Geany_, the browser plugin `Markdown Viewer`_ and even a dedicated markdown editor like Marktext_, retext_ and ghostwriter_.
     26 
     27 And of course, there exist SaaS offerings such as hackmd_. But seeing as those are not alternatives for offline use, we don't concern ourselves with those here.
     28 
     29 But what is the equivalent of sxiv_ or feh_ for Markdown?
     30 
     31 Honestly, I couldn't find any such thing. If there is, I'd `love to know`_.
     32 
     33 Fortunately, there is a perfectly reasonable workaround.
     34 
     35 
     36 Step by step
     37 ============
     38 
     39 After all, it is in the spirit of \*nixes to use a choice of tools who *does one thing and does it well*.
     40 
     41 So, no matter how bizarre it feels, it may make sense that a lurid format like *Markdown* should be treated in a separate step to produce a more well-established - and less ambiguous - format.
     42 
     43 I asked a related question on Stackexchange_ long ago, and there the ``pandoc`` tool came up as a solution.
     44 
     45 And it turns out it works beautifully in this case aswell.
     46 
     47 Consider the following script:
     48 
     49 .. code-block:: bash
     50 
     51         t=$(mktemp --suffix=.html)
     52         2>&1 echo $t
     53         pandoc -f gfm -t html -M document-css=false --standalone $1 > $t 2> /dev/null
     54         w3m $t
     55 
     56 
     57 Quite simply, we generate a standalone html file in ``tmpfs``, which in turn is read and renderered by a web browser.
     58 
     59 
     60 Browsing browsers
     61 =================
     62 
     63 No appreciation for w3m_, eh? Instead want that fuzzy feel of comforting colors, smooth scroll and fancy fonts?
     64 
     65 I can understand. I used to suffer that affliction, too.
     66 
     67 But nonetheless; it's an important point. For example, what if I wanted to use lynx_ or vimb_ instead? Choosing the browser to view with should definitely be the caller's call.
     68 
     69 Is there a canonical way of doing that in Linux.
     70 
     71 Kind of.
     72 
     73 Let's review a couple of the options.
     74 
     75 
     76 The environmental solution
     77 --------------------------
     78 
     79 Some applications honor the ``$BROWSER`` environment variable. So let's cover for that:
     80 
     81 .. code-block:: bash
     82 
     83         browser=${BROWSER:-w3m}
     84         t=$(mktemp --suffix=.html)
     85         pandoc -f gfm -t html -M document-css=false --standalone $1 > $t 2> /dev/null
     86         $browser $t
     87 
     88 Now, viewing the markdown file ``README.md`` with ``lynx`` is as easy as:
     89 
     90 .. code-block:: console 
     91 
     92         $ BROWSER=$(which lynx) bash wmd.sh README.md
     93 
     94 
     95 The cross solution
     96 ------------------
     97 
     98 So, have you heard about the Cross Desktop Group? [1]_ These are the guys you should be sending a thought of gratitude every time you intuitively look in your ``~/.config`` or ``~/.local`` folder for application data. And if you're a **HUIf** [2]_ coder, chances are you have seen the string ``xdg`` in some function call somewhere.
     99 
    100 These days they go by the name `Free Desktop Group`_, and among other things they have negotiated something particularly useful to us in this particular case.
    101 
    102 To launch programs in a desktop environment in Linux, a `Desktop Entry Specification`_ file format [3]_ has been defined - appropriately suffixed ``.desktop``.
    103 
    104 Take a look at a random ``*.desktop`` file in ``/usr/share/applications`` (your most likely default ``$XDG_DATA_DIRS`` location, where xdg searches for desktop files). Every one will contain an top-level ``Exec=`` entry.
    105 
    106 For example, my ``feh.desktop`` entry has ``Exec=feh --start-at %u`` which means find ``feh`` in ``$PATH`` and execute it with the ``--start-at`` switch and one single *url* as argument:
    107 
    108 .. code-block:: ini
    109 
    110         [Desktop Entry]
    111         Name=Feh
    112         Name[en_US]=feh
    113         GenericName=Image viewer
    114         GenericName[en_US]=Image viewer
    115         Comment=Image viewer and cataloguer
    116         Exec=feh --start-at %u
    117         Terminal=false
    118         Type=Application
    119         Icon=feh
    120         Categories=Graphics;2DGraphics;Viewer;
    121         MimeType=image/bmp;image/gif;image/jpeg;image/jpg;image/pjpeg;image/png;image/tiff;image/webp;image/x-bmp;image/x-pcx;image/x-png;image/x-portable-anymap;image/x-portable-bitmap;image/x-portable-graymap;image/x-portable-pixmap;image/x-tga;image/x-xbitmap;image/heic;
    122         NoDisplay=true
    123 
    124 Covering this case in our script:
    125 
    126 .. code-block:: bash
    127 
    128         fallbackbrowsercmd=w3m
    129         browsercmd=
    130         # if browser env exists, then
    131         # try handle it as a desktop entry
    132         if [ ! -z "$BROWSER" ]; then
    133                 # find the xdg paths
    134                 XDG_DATA_DIRS=${XDG_DATA_DIRS:-/usr/share}
    135                 if [ ! -z "$XDG_DATA_HOME" ]; then
    136                         XDG_DATA_DIRS=$XDG_DATA_HOME:$XDG_DATA_DIRS
    137                 fi
    138                 # split on ":" and try dirs one by one
    139                 # use first matching browser desktop entry
    140                 _ifs=$IFS
    141                 IFS=:
    142                 dirs=("$XDG_DATA_DIRS")
    143                 for d in $dirs; do
    144                         s=$BROWSER.desktop
    145                         a=$d/applications/$BROWSER.desktop 
    146                         if [ -f "$a" ]; then
    147                                 browsercmd="gtk-launch $s"
    148                         fi
    149                 done
    150                 IFS=
    151         fi
    152         # if no browser set or could not be found in xdg,
    153         # then try the browser env var as command, or
    154         # ultimately the static fallback
    155         if [ -z "$browsercmd" ]; then
    156                 browsercmd=${BROWSER:-$fallbackbrowsercmd}
    157         fi
    158         t=$(mktemp --suffix=.html)
    159         pandoc -f gfm -t html -M document-css=false --standalone $1 > $t 2> /dev/null
    160         $browsercmd $t
    161 
    162 
    163 The default solution
    164 --------------------
    165 
    166 Yes, there is such a thing as "default web browser" in ``XDG``, too.
    167 
    168 Have a look at ``xdg-settings --list``. On my system, it shows a rather modest output:
    169 
    170 .. code-block:: console
    171 
    172         $ xdg-settings --list
    173         Known properties:
    174                 default-url-scheme-handler    Default handler for URL scheme
    175                 default-web-browser           Default web browser
    176 
    177 And the default web browser is:
    178 
    179 .. code-block:: console
    180 
    181         $ xdg-settings get default-web-browser
    182         brave-browser.desktop 
    183 
    184 Yeah, yeah, yeah. Busted. I use graphical browsers, too.
    185 
    186 Now let's add this to the mix, then.
    187 
    188 .. code-block:: bash
    189 
    190         fallbackbrowsercmd=w3m
    191         browsercmd=
    192         # if browser env var not set, set it with default browser
    193         if [ -z "$BROWSER" ]; then
    194                 BROWSER=$(xdg-settings get default-web-browser)
    195                 BROWSER=${BROWSER%%.*}
    196         fi
    197         if [ ! -z "$BROWSER" ]; then
    198                 XDG_DATA_DIRS=${XDG_DATA_DIRS:-/usr/share}
    199                 if [ ! -z "$XDG_DATA_HOME" ]; then
    200                         XDG_DATA_DIRS=$XDG_DATA_HOME:$XDG_DATA_DIRS
    201                 fi
    202                 _ifs=$IFS
    203                 IFS=:
    204                 dirs=("$XDG_DATA_DIRS")
    205                 for d in $dirs; do
    206                         s=$BROWSER.desktop
    207                         a=$d/applications/$BROWSER.desktop 
    208                         if [ -f "$a" ]; then
    209                                 browsercmd="gtk-launch $s"
    210                         fi
    211                 done
    212                 IFS=
    213         fi
    214         if [ -z "$browsercmd" ]; then
    215                 browsercmd=${BROWSER:-$fallbackbrowsercmd}
    216         fi
    217         t=$(mktemp --suffix=.html)
    218         pandoc -f gfm -t html -M document-css=false --standalone $1 > $t 2> /dev/null
    219         $browsercmd $t
    220 
    221 
    222 Naming the executioner
    223 ======================
    224 
    225 So now we have a markdown viewer. And it can be as lean or as heavy as you want it to be. It's all to the browser you choose.
    226 
    227 To make it accessible, map the script as a command alias in your ``.profile`` or ``.bashrc`` and you have the viewer at your fingertips.
    228 
    229 I call mine ``wmd``:
    230 
    231 
    232 .. code-block:: bash
    233 
    234         alias wmd="bash $HOME/scripts/markdown.sh"
    235 
    236 And voilá:
    237 
    238 .. code-block:: console
    239 
    240         $ export BROWSER=lynx
    241         $ wmd README.md
    242 
    243 ..
    244 
    245                 .. [1] Or X Desktop Group, as they were originally called.
    246 
    247 
    248 ..
    249 
    250                 .. [2] Human User Interface. Don't bother looking - I made it up. As the cause of AI, robots and machines inevitably will become appropriated by the woke intersectionality complex, the terminology is probably going to need such distictions.
    251 
    252 
    253 ..
    254 
    255                 .. [3] Actually the format is ini_. The standard is rather in the file naming, really.
    256 
    257 .. _Free Desktop Group: https://www.freedesktop.org/
    258 
    259 .. _Desktop Entry Specification: https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
    260 
    261 .. _Atom: https://atom-editor.cc/
    262 
    263 .. _Geany: https://plugins.geany.org/markdown.html
    264 
    265 .. _Markdown Viewer: https://chromewebstore.google.com/detail/markdown-viewer/ckkdlimhmcjmikdlpkmbgfkaikojcbjk
    266 
    267 .. _Marktext: https://github.com/marktext/marktext
    268 
    269 .. _Stackexchange: https://tex.stackexchange.com/questions/341899/latex-to-markdown-converter
    270 
    271 .. _w3m: https://w3m.sourceforge.net
    272 
    273 .. _lynx: https://lynx.browser.org
    274 
    275 .. _vimb: https://fanglingsu.github.io/vimb
    276 
    277 .. _sxiv: https://github.com/muennich/sxiv
    278 
    279 .. _feh: https://feh.finalrewind.org
    280 
    281 .. _hackmd: https://hackmd.io
    282 
    283 .. _love to know: https://holbrook.no/msg
    284 
    285 .. _ini: https://en.wikipedia.org/wiki/INI_file
    286 
    287 .. _retext: https://github.com/retext-project/retext
    288 
    289 .. _ghostwriter: https://ghostwriter.kde.org/