documentation
overview
halp works by translating directories of files into pages - either Gemini or Web pages.
I intended the experience of halp to be as close to using simple, familiar, Unix tools as possible. Pages are defined by directories. Content is described in plain HTML or Gemtext. Titles, descriptions, etc. live in dotfiles. Content history (for Atom feeds) is extracted from Git logs.
getting started
halp requires very few files to get started. Here's the minimum directory structure necessary to generate both Web and Gemini content:
$ tree -a
.
├── gemini
│ └── .gitkeep
├── halp-config.pl
├── tmp
│ ├── gemini
│ │ └── .gitkeep
│ └── web
│ └── .gitkeep
└── web
└── .gitkeep
It is necessary that the above files and directories be committed to Git (hence the .gitkeep files). halp relies upon Git history to generate Atom feeds for content, as I'll explain later.
The configuration file, halp-config.pl, is a Perl file containing the Website and Gemini Capsule configuration:
{
author => {
email => 'alice@example.com',
name => 'Alice'
},
domain => 'example.com',
gemini => {
host => 'http://example.com:8080',
input_path => '.gemini',
output_path => './tmp/gemini'
},
web => {
host => 'http://example.com:8080',
input_path => '.web',
output_path => './tmp/web'
}
}
To generate both the static Gemini capsule and Website from the above configuration and directory structure, you'd run:
$ perl ./halp.pl --config-filename ./halp-config.pl --gemini
$ perl ./halp.pl --config-filename ./halp-config.pl --web
This produces a simple Website and Gemini capsule that you can deploy by whatever means you choose (self-hosting with Caddy or Molly Brown, through a CDN, etc.):
tmp
├── gemini
│ ├── .gitkeep
│ ├── .static
│ │ └── images
│ │ ├── atom.png
│ │ ├── bvbrows.gif
│ │ ├── favicon.png
│ │ ├── halp-icon.jpg
│ │ ├── mothracompat.gif
│ │ ├── valid-atom.png
│ │ ├── valid-html5.png
│ │ └── vcss-blue
│ ├── feed.xml
│ ├── index.gmi
│ └── robots.txt
└── web
├── .gitkeep
├── .static
│ ├── images
│ │ ├── atom.png
│ │ ├── avatar.jpg
│ │ ├── bvbrows.gif
│ │ ├── favicon-128x128.png
│ │ ├── favicon-16x16.png
│ │ ├── favicon-256x256.png
│ │ ├── favicon-32x32.png
│ │ ├── favicon-512x512.png
│ │ ├── favicon-64x64.png
│ │ ├── favicon-96x96.png
│ │ ├── favicon-apple-touch-icon-152x152.png
│ │ ├── favicon-apple-touch-icon-167x167.png
│ │ ├── favicon-apple-touch-icon-180x180.png
│ │ ├── favicon.ico
│ │ ├── halp-icon.jpg
│ │ ├── mothracompat.gif
│ │ ├── valid-atom.png
│ │ ├── valid-html5.png
│ │ └── vcss-blue
│ └── styles
│ └── halp.css
├── feed.xml
├── index.html
└── robots.txt
here's one I prepared earlier
The halp website, halp.fyi is, of course, built with halp! Probably the easiest way of learning halp is just to take a look at the configuration and input directory used to build it:
- Configuration: https://git.sr.ht/~duncan-bayne/halp/tree/master/item/site-config/halp-config.pl
- Web: https://git.sr.ht/~duncan-bayne/halp/tree/master/item/site/web
- Gemini: https://git.sr.ht/~duncan-bayne/halp/tree/master/item/site/gemini
how halp works
For both Gemini and Web content, halp takes the same approach:
- It generates a menu from the root directory, listing the pages and subdirectories of the root directory.
- For each non-hidden directory, it generates an index page listing the directory contents.
- For each non-hidden directory, it generates an Atom feed (
feed.xml). - For each content file (
.gmifor Gemini,.htmlfor Web), it renders that content into a template that includes the menu. - For each non-content file (e.g. images and binaries), it simply copies the file into the output directory.
Note that hidden files (i.e. those beginning with .) are not copied into the output directory, but hidden directories are.
titles and descriptions
If a directory contains a .description.html or .description.gmi file, that description is included in the index page before the directory contents. If a directory contains a .title file, the contents of that file will be used as the title instead of the directory name. E.g.:
$ tree -a
.
├── .description.html
└── .title
If a file has a matching dotfile containing a title, that will be used for the title instead of the filename. E.g.:
$ ls .*public* *public*
.2023-01-06-public-inbox-experiment.gmi.title
2023-01-06-public-inbox-experiment.gmi
static files
halp copies the following default static files into all generated Websites:
.
├── .static
│ └── images
│ ├── atom.png
│ ├── bvbrows.gif
│ ├── favicon.png
│ ├── halp-icon.jpg
│ ├── mothracompat.gif
│ ├── valid-atom.png
│ ├── valid-html5.png
│ └── vcss-blue
└── robots.txt
... and it copies the following default static files into all generated Gemini capsules:
.
├── .static
│ ├── images
│ │ ├── atom.png
│ │ ├── avatar.jpg
│ │ ├── bvbrows.gif
│ │ ├── favicon-128x128.png
│ │ ├── favicon-16x16.png
│ │ ├── favicon-256x256.png
│ │ ├── favicon-32x32.png
│ │ ├── favicon-512x512.png
│ │ ├── favicon-64x64.png
│ │ ├── favicon-96x96.png
│ │ ├── favicon-apple-touch-icon-152x152.png
│ │ ├── favicon-apple-touch-icon-167x167.png
│ │ ├── favicon-apple-touch-icon-180x180.png
│ │ ├── favicon.ico
│ │ ├── halp-icon.jpg
│ │ ├── mothracompat.gif
│ │ ├── valid-atom.png
│ │ ├── valid-html5.png
│ │ └── vcss-blue
│ └── styles
│ └── halp.css
└── robots.txt
In either case, if your input directory contains any of those files, the file from your input directory will be copied instead of the default static file. So e.g. you could include ./robots.txt in your input directory, and that would be what gets included instead of the default halp ./robots.txt.
atom feeds
Atom feeds are generated using timestamps from Git. Theupdated value for each entry is the last modified time for the input file, according to git.
This has two consequences:
- All files in your input directory must be checked in to Git.
- If you build your Website or Gemini capsule before you check all your files into Git, the entry for files not checked in will have an
updatedvalue of the Unix epoch (Thursday, 1 January 1970 12:00:00 AM).
templates
halp has a set of simple text templates (courtesy the Text::Template library) that it uses for Gemini and Web content:
web/templates/
├── directory_listing.html.tpl
├── menu.html.tpl
└── page.html.tpl
gemini/templates/
├── directory_listing.gmi.tpl
├── menu.gmi.tpl
└── page.gmi.tpl
They are the same for all pages; customisation of title, description (in the case of directories), is done via dotfiles as described in titles and descriptions above.
menus
Every halp page contains a menu (inspired by Brad Taunt's hamburgers post) at the bottom. The menu consists of links to:
- The top of the page (for Web pages only; in-page anchors are not supported by Gemini).
- The top-level page.
- Each of the immediate children of the top-level page.
- The Atom feed for the current page.
- The halp website.