site

Website Content
git clone https://git.pngu.org/site
Log | Files | Refs | README

programs.html (24490B)


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
<!doctype html>
<html lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Programs &amp; Utilities</title>
    <script src="header.js"></script>
  </head>
  <body>
    <a style="text-decoration: none" href="content.html">&lt;--</a>
    <h2>Programs &amp; Utilities</h2>
    <p><i>2026-01-17</i></p>
    <hr />
    This page contains a list of various packages, programs, and utilities that I use across most of
    my systems, along with some relevant configuration and nuances.
    <h3>Shell</h3>
    <ul>
      <li>
        <a href="https://github.com/sharkdp/bat" target="_blank">bat</a>
        <p>
          A <code>cat</code> alternative with line numbers and syntax highlighting. The
          <code>--all</code> flag is especially helpful for showing spaces, tabs, and line endings
          in files. The only configuration really needed is setting the theme and perhaps more
          preferentially, an alias that also disables paging, like normal <code>cat</code> output:
        </p>
        <pre><code>export BAT_THEME="base16"
alias cat="bat --paging=never"</code></pre>
      </li>
      <li>
        <a href="https://direnv.net" target="_blank">direnv</a>
        <p>
          Allows for a shell script to be run upon entering a directory. This is particularly useful
          for automatically activating Python virtual environments. Configuration consists of
          creating the
          <code>.envrc</code> file that is sourced at entry, and allowing this to happen by running
          <code>direnv allow</code> within the directory.
        </p>
        <pre><code class="language-shell">if [ -f ".venv/bin/activate" ]; then
  source .venv/bin/activate
fi</code></pre>
      </li>
      <li>
        <a href="https://github.com/lsd-rs/lsd" target="_blank">lsd</a>
        <p>
          A <code>ls</code> alternative that mostly provides intuitive colours and icons. As for
          configuration, I found it beneficial to
          <a href="https://git.pngu.org/dots/tree/.config/lsd/config.yaml" target="_blank"
            >trim down</a
          >
          some of the default output, and
          <a href="https://git.pngu.org/dots/tree/.config/lsd/colors.yaml" target="_blank"
            >configure colours</a
          >
          accordingly.
        </p>
      </li>
      <li>
        <a href="https://neovim.io" target="_blank">neovim</a>
        <p>
          My Neovim configuration is based on
          <a href="https://nvchad.com" target="_blank">Nvchad</a>, and configured with my desired
          language servers, formatters, and plugins.
          <a href="https://mason-registry.dev" target="_blank">Mason</a> serves a package manager
          for Neovim packages. I use the following LSPs in
          <a href="https://github.com/neovim/nvim-lspconfig" target="_blank">nvim-lspconfig</a>:
        </p>
        <ul>
          <li>
            <p>
              <a href="https://clangd.llvm.org" target="_blank">Clangd</a>: for C/C++. Works well,
              despite sometimes falling over when interpreting division (<code>/</code>) as trying
              to reference a path. The <code>HeaderInsertion: Never</code> directive on macOS does
              not seem to work, too.
            </p>
          </li>
          <li>
            <p>
              <a href="https://github.com/microsoft/vscode-css-languageservice" target="_blank"
                >CSS LSP</a
              >: for CSS. No complaints, beside the Node.js dependency.
            </p>
          </li>
          <li>
            <p>
              <a href="https://github.com/microsoft/vscode-html-languageservice" target="_blank"
                >HTML LSP</a
              >: for HTML. Helps cut the typing needed for working with HTML tags in half. Also
              depends on Node.js.
            </p>
          </li>
          <li>
            <p>
              <a href="https://docs.basedpyright.com/latest" target="_blank">Basedpyright</a>: for
              Python. Very robust and works fantastically alongside Ruff.
            </p>
          </li>
          <li>
            <p>
              <a href="https://github.com/LuaLS/lua-language-server" target="_blank">Lua LSP</a>:
              for Lua. No complaints, fast, minimal dependencies.
            </p>
          </li>
          <li>
            <p>
              <a href="https://chipsalliance.github.io/verible" target="_blank">Verible</a>: for
              SystemVerilog and related languages. A rather robust package that contains linting and
              formatting as well.
            </p>
          </li>
        </ul>
        I use the following formatters in
        <a href="https://github.com/stevearc/conform.nvim/tree/master" target="_blank"
          >conform.nvim</a
        >:
        <ul>
          <li>
            <p>
              <a href="https://clang.llvm.org/docs/ClangFormat.html">ClangFormat</a>: for C/C++.
              Works well, part of standard Clang software bundle. However, this is not the case on
              macOS, and ClangFormat must be installed explicitly.
            </p>
          </li>
          <li>
            <p>
              <a href="https://oxc.rs" target="_blank">Oxfmt</a>: for CSS, HTML, &amp; Markdown.
              Preferable over the better-known <a href="https://prettier.io">Prettier</a> or
              <a href="https://biomejs.dev" target="_blank">Biome</a>, being much faster.
              Unfortunately still depends on Node.js.
            </p>
          </li>
          <li>
            <p>
              <a href="https://github.com/JohnnyMorganz/StyLua" target="_blank">Stylua</a>: for Lua.
              Does what it says on the tin.
            </p>
          </li>
          <li>
            <p>
              <a href="https://docs.astral.sh/ruff" target="_blank">Ruff</a>: for Python. Rather
              robust and very fast. Capable of fixing code (e.g. removing an accidental trailing
              semicolon), formatting code, and sorting Python <code>import</code>'s.
            </p>
          </li>
          <li>
            <p>
              <a href="https://chipsalliance.github.io/verible/verilog_format.html" target="_blank"
                >Verible Format</a
              >: for SystemVerilog and related languages. Formatter works well, however disagrees
              with some platforms I use, such as
              <a href="https://chipdev.io" target="_blank">chipdev.io</a>, which requires manual
              intervention.
            </p>
          </li>
        </ul>
      </li>
      <li>
        <a href="https://github.com/BurntSushi/ripgrep" target="_blank">ripgrep</a>
        <p>
          An incredibly robust <code>grep</code> alternative, which I am quite sure I do not use to
          its full potential. Things like omitting directories are especially useful, and also
          intuitive &mdash; e.g. <code>rg -g '!dir/' foo</code>.
        </p>
      </li>
      <li>
        <a href="https://starship.rs" target="_blank">starship</a>
        <p>
          An alternative terminal prompt to <code>$</code> that has extensive customisation and
          functionality regarding current working directory. I find the virtual environment status
          very useful.
        </p>
      </li>
      <li>
        <a href="https://github.com/XAMPPRocky/tokei" target="_blank">tokei</a>
        <p>Counts lines of code, as well as different languages, comments, etc.</p>
      </li>
      <li>
        <a href="https://github.com/ajeetdsouza/zoxide" target="_blank">zoxide</a>
        <p>
          A <code>cd</code> alternative that auto-jumps to previously visited directories. This
          turns a long <code>cd to/a/nested/directory/named/foo</code> command to just
          <code>cd foo</code>, after entering it once.
        </p>
      </li>
      <li>
        zsh
        <p>My shell-specific configuration is rather minimal:</p>
        <pre><code class="language-shell">#!/bin/zsh

export HISTFILE=~/.zsh_history
export HISTSIZE=20000
export SAVEHIST=20000
export LANG=en_GB.UTF-8

path+=(
"$HOME/.local/bin"
"$path[@]")
export PATH

setopt HIST_IGNORE_ALL_DUPS
setopt HIST_FIND_NO_DUPS

bindkey -v
bindkey "^?" backward-delete-char
zstyle ':completion:*' completer _complete _ignored
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Za-z}'
zstyle :compinstall filename '~/.zshrc'
autoload -U compinit && compinit</code></pre>
        <p>
          ZSH's default Vi mode has room for improvement unfortunately, but it is usable
          nonetheless.
        </p>
      </li>
      <ul>
        <li>
          <a href="https://github.com/zsh-users/zsh-autosuggestions" target="_blank"
            >zsh-autosuggestions</a
          >
          <p>
            Similarly to Zoxide mentioned above, this plugin aides in running previously long
            commands. Instead of typing out a cumbersome
            <code>awk</code> command that you've run before, one can tap the right arrow key to fill
            the command after typing the first few characters.
          </p>
          <p>
            Depending on your terminal's colour configuration, the default colour of the suggested
            text to fill may not be very readable. If so, set it manually at shell startup:
          </p>
          <pre><code>export ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#C7C7C7"</code></pre>
        </li>
        <li>
          <a href="https://github.com/zsh-users/zsh-syntax-highlighting" target="_blank"
            >zsh-syntax-highlighting</a
          >
          <p>
            Provides syntax highlighting directly to commands run in your shell. A small detail but
            invaluable for things like underlining a path that is valid, quoted and conditional
            arguments, and so on.
          </p>
        </li>
      </ul>
    </ul>
    <h3>Graphical</h3>
    <p>
      My non-headless Linux systems run under Wayland, so in turn, the following applications are
      more or less Wayland specific.
    </p>
    <ul>
      <li>
        <a href="https://codeberg.org/dnkl/fuzzel" target="_blank">fuzzel</a>
        <p>
          Application launcher that is incredibly easy to
          <a href="https://git.pngu.org/dots/tree/.config/fuzzel/fuzzel.ini" target="_blank"
            >configure</a
          >.
        </p>
      </li>
      <li>
        <a href="https://gitlab.com/chinstrap/gammastep" target="_blank">gammastep</a>
        <p>
          A fork of the
          <a href="https://github.com/jonls/redshift" target="_blank">Redshift</a> program that
          adjusts screen temperature (optionally, at sunset). Gammastep is also Wayland-compatible.
          It can be enabled based on simply time or sunset/sunrise times in a given
          latitude/longitude. For the latter, geographical coordinates can be manually set, or
          <a href="https://github.com/erfanoabdi/geoclue" target="_blank">Geoclue</a> can be used to
          determine location automatically, which is suitable for a laptop. Gammastep's defaults
          work well for me, and it can simply be started by Sway:
        </p>
        <pre><code>exec /usr/bin/gammastep -l 52.5200:13.4050</code></pre>
      </li>
      <li>
        <a href="https://gitlab.freedesktop.org/emersion/grim" target="_blank">grim</a> +
        <a href="https://github.com/emersion/slurp" target="_blank">slurp</a>
        <p>
          Screenshot tools that are boring and functional. Grim is the screenshot utility itself,
          while Slurp allows for selecting a region to capture. The combination is easily bound to
          Sway shortcuts to either copy to clipboard (see
          <a href="https://github.com/bugaevc/wl-clipboard" target="_blank">wl-clipboard</a>), or
          save to <code>$XDG_PICTURES_DIR</code>, respectively:
        </p>
        <pre><code>bindsym $mod+p exec       grim -g "$(slurp)" - | wl-copy
bindsym $mod+Shift+p exec grim -g "$(slurp)"</code></pre>
        <p></p>
      </li>
      <li>
        <a href="https://github.com/jovanlanik/gtklock" target="_blank">gtklock</a>
        <p>
          A GTK-based lock screen program that's useful when I'm out and about.
          <a href="https://git.pngu.org/dots/tree/.config/gtklock" target="_blank">Configuration</a>
          is rather simple, and it is styled via CSS, which I find to be a perk. Also triggered from
          a Sway mapping:
        </p>
        <pre><code>bindsym $mod+Escape  exec gtklock -d</code></pre>
      </li>
      <li>
        <a href="https://sr.ht/~exec64/imv" target="_blank">imv</a>
        <p>
          An image viewer that supports the file formats I encounter, and even has Vim keybindings.
        </p>
      </li>
      <li>
        <a href="https://mako-project.org" target="_blank">mako</a>
        <p>
          Notification daemon that's libnotify compatible, and easily
          <a href="https://git.pngu.org/dots/tree/.config/mako/config" target="_blank">configured</a
          >. Its daemon must be started by Sway:.
        </p>
        <pre><code>exec /usr/bin/mako</code></pre>
      </li>
      <li>
        <a href="https://fonts.google.com/noto" target="_blank">noto fonts</a>
        (+ cjk &amp; emoji)
        <p>
          Incredibly widely used fonts, including support for CJK and Cyrillic languages, and emoji
          support in applications such as Discord.
        </p>
      </li>
      <li>
        <a href="https://swaywm.org" target="_blank">sway</a>
        <p>
          Rather powerful window manager. Especially appreciate the input
          <a href="https://git.pngu.org/dots/tree/.config/sway/config" target="_blank"
            >configuration</a
          >
          options like easily changing trackpad behaviours and switching between languages. Things
          like these are challenging on X. I use
          <a href="https://github.com/swaywm/swaybg" target="_blank">swaybg</a>
          to set wallpapers.
        </p>
      </li>
      <li>
        <a href="https://github.com/Alexays/Waybar" target="_blank">waybar</a>
        <p>
          Status bar with loads of functionality.
          <a href="https://git.pngu.org/dots/tree/.config/sway/config" target="_blank"
            >Configuration</a
          >
          is done via a JSON-esque file and styled via CSS, which offers nearly too much
          opportunity.
        </p>
      </li>
      <li>
        <a href="https://github.com/wez/wezterm" target="_blank">wezterm</a>
        <p>
          Terminal of choice. I went back and forth between this, and
          <a href="https://git.pngu.org/st" target="_blank">st</a>, and while fun, st lacks Wayland
          and macOS support. Wezterm is easily
          <a href="https://git.pngu.org/dots/tree/.config/wezterm/wezterm.lua">configured</a>
          in Lua.
        </p>
      </li>
      <li>
        <a href="https://pwmt.org/projects/zathura" target="_blank">zathura</a>
        <p>
          PDF viewer with Vim keybindings and commands. Allows for ample
          <a href="https://git.pngu.org/dots/tree/.config/zathura/zathurarc" target="_blank"
            >configuration</a
          >
          of colours and fonts.
        </p>
      </li>
    </ul>
    <h3>System</h3>
    <ul>
      <li>
        <a href="https://chrony-project.org" target="_blank">chrony</a>
        <p>
          The system clock frequently falls out of sync on some of my older hardware and Chrony is a
          solution. Enable the system service and select a
          <a href="https://www.ntppool.org/zone/@" target="_blank">NTP pool</a>.
        </p>
      </li>
      <li>
        <a href="https://github.com/dracut-ng/dracut-ng" target="_blank">dracut</a>
        <p>
          On my systems, Dracut is used to generate initramfs during kernel installation, and create
          a bootable
          <a href="https://uapi-group.org/specifications/specs/unified_kernel_image" target="_blank"
            >UKI</a
          >, which omits the need for a bootloader, such as GRUB. See system-specific pages for more
          details.
        </p>
      </li>
      <li>
        <a href="https://github.com/rhboot/efibootmgr" target="_blank">efibootmgr</a>
        <p>
          Similar to usage of the above, efibootmgr is used to create boot entries for bootable
          kernel images, both in the form of UKI's or
          <a href="https://www.kernel.org/doc/html/latest/admin-guide/efi-stub.html" target="_blank"
            >EFI Stub</a
          >'s. Also handy for cleaning up other entries littered around by other operating systems.
        </p>
      </li>
      <li>
        <a href="https://git.kernel.org/cgit/network/wireless/iwd.git" target="_blank">iwd</a>
        <ul>
          <li>
            <a href="https://roy.marples.name/projects/openresolv" target="_blank">openresolv</a>
          </li>
        </ul>
        <p>
          Preferred networking setup for its minimal system footprint, reliability (significantly
          over WPA Supplicant), and ease of use with most networks I encounter. It can be configured
          to use its internal DHCP client, and a DNS manager such as a resolvconf:
        </p>
        <pre><code>[General]
EnableNetworkConfiguration=true

[Network]
NameResolvingService=resolvconf</code></pre>
      </li>
      <li>
        <a href="https://github.com/Duncaen/OpenDoas" target="_blank">doas</a>
        <p>
          A <code>sudo</code> alternative that is a fraction of the size of the traditional
          authentication program, and performs everything most single-user, non-server systems would
          need. Configuration to allow persistent authentication for some time after a user of the
          <code>wheel</code> group has entered their password is straight forward:
        </p>
        <pre><code>permit persist :wheel</code></pre>
      </li>
      <li>
        <a href="https://github.com/rvaiya/keyd" target="_blank">keyd</a>
        <p>
          Key remapping for Linux systems that's capable of reproducing much of my
          <a href="https://qmk.fm" target="_blank">QMK</a> configuration, which primarily includes
          combos, layers, home row mods, and more. This is certainly suitable for making my laptop's
          keyboard usable, albeit uncomfortable.
          <a href="https://git.pngu.org/dots/tree/.config/keyd.conf"></a> is certainly straight
          forward as well. This file must however either live at or be symlinked to
          <code>/etc/keyd/default.conf</code>.
        </p>
      </li>
      <li>
        <a href="https://github.com/OpenRC/openrc" target="_blank">openrc</a>
        <p>
          Undoubtedly my preferred init system. Incredibly reliable and not built such that things
          like reboot depend on some other program (hint hint systemd...). Usage is also intuitive:
        </p>
        <pre><code>$ rc-status
Runlevel: default
 chrony                                                                 [  started  ]
 dbus                                             [  started 17 day(s) 23:13:07 (0) ]
 thermald                                         [  started 17 day(s) 23:13:06 (0) ]
 sshd                                             [  started 17 day(s) 23:13:06 (0) ]
 iwd                                               [  started 3 day(s) 01:25:06 (0) ]
 keyd                                                                   [  started  ]
 tlp                                                                    [  started  ]
 agetty.tty1                                      [  started 17 day(s) 23:13:05 (0) ]
Dynamic Runlevel: hotplugged
 user.jack                                        [  started 17 day(s) 23:12:47 (0) ]
        </code></pre>
      </li>
    </ul>
    <h3>Development</h3>
    <ul>
      <li>
        <a href="https://github.com/rizsotto/Bear" target="_blank">bear</a>
        <p>
          The clangd language server sources a
          <code>compile_commands.json</code> file that provides information like where header files
          are found, compilation flags, etc. Clang(d) has no built-in provision for generating this
          file, so bear is the tool for the job. Usage is rather straight forward:
        </p>
        <pre><code>$ bear -- cc misc.c
$ cat compile_commands.json
[
  {
    "file": "misc.c",
    "arguments": [
      "cc",
      "misc.c"
    ],
    "directory": "/home/jack"
  }
]</code></pre>
        <p>
          Running <code>bear -- make</code> would be appropriate for projects that use a Makefile.
        </p>
      </li>
      <li>
        <a href="https://lldb.llvm.org" target="_blank">lldb</a>
        <p>
          A debugger that behaves similarly to GDB, but is much more intuitive in my opinion. This
          <a href="https://github.com/gdbinit/lldbinit" target="_blank">here</a>
          extends LLDB a bit by providing some graphical features and overall more information by
          default. The script lives in
          <code>~/.local/lldbinit.py</code> and call it from <code>.lldbinit</code>:
        </p>
        <pre><code>command script import ~/.local/lldbinit.py</code></pre>
      </li>
      <li>
        <a href="https://github.com/pkgconf/pkgconf" target="_blank">pkgconf</a>
        <p>
          Used to automatically configure compiler and linker flags for libraries that need them.
          Helpful working on projects across different systems and architectures.
        </p>
      </li>
      <li>
        <a href="https://github.com/astral-sh/uv" target="_blank">uv</a>
        <p>
          Python installation and package manager. Incredibly fast and intuitive to use, and
          preferable to <code>pip</code> and other Python utilities.
        </p>
      </li>
    </ul>
    <h3>Miscellaneous</h3>
    <ul>
      <li>
        <a href="https://github.com/wwmm/easyeffects" target="_blank">easy effects</a>
        <p>
          At the desktop, I use a Schiit
          <a href="https://www.schiit.com/products/bifrost" target="_blank">Bifrost DAC</a>
          and
          <a href="https://www.schiit.com/products/jotunheim-3" target="_blank">Jotunheim amp</a>
          for headphone audio. Easy Effects has a range of audio plugins, including a parametric
          equaliser.
          <a href="https://github.com/jaakkopasanen/AutoEq" target="_blank">AutoEQ</a>
          provides easy equalising for nearly all headphones and is a fantastic resource.
        </p>
      </li>

      <li>
        <a href="https://eyed3.readthedocs.io" target="_blank">eyed3</a>
        <p>
          I keep a collection of some music, and eyeD3 allows setting the metadata associated a
          given MP3 file. It is quite robust and allows configuring most fields one may need:
        </p>
        <pre><code>eyeD3 --remove-all -t="Song Title" -a="Artist" --add-image="cover.png:FRONT_COVER" audio_file.mp3</code></pre>
        <p>
          This example removes other metadata, sets the song title and artist, and adds a front
          album cover image.
        </p>
      </li>
      <li>
        <a href="https://www.imagemagick.org" target="_blank">imagemagick</a>
        <p>
          I occasionally find myself in need of some sort of basic image manipulation, like
          rotating, resizing, or combining images. Searching the web for "rotate image online" is
          silly, and Imagemagick is capable of far more than just that.
        </p>
      </li>
      <li>
        <a href="https://www.weechat.org" target="_blank">weechat</a>
        <p>
          I am active in a few IRC channels, however not often enough to need a fancy GUI client.
          Weechat is quite configurable and easy to use, with much of the shortcuts match those
          found in browsers and Vim.
        </p>
      </li>
      <li>
        <a href="https://github.com/yadm-dev/yadm" target="_blank">yadm</a>
        <p>
          This tool (read git wrapper) keeps track of all my system configuration files with the
          ease and convenience of git. Each system gets a branch and files I want to exclude are
          excluded via the standard <code>.gitignore</code>. This makes setting up new systems or
          undoing changes incredibly easy.
        </p>
      </li>
    </ul>
    <script src="frame.js"></script>
  </body>
</html>