docs: rewrite README with architecture section + full make reference
The README now documents:
- Architecture: the static build/serving model, the content -> public/
-> pagefind/ data flow, the SCSS compile pipeline (why the Hugo
extended build is required, SRI fingerprinting), where
customizations live (_variables.scss table, search.html), the
data-pagefind-body index-scoping mechanism, the #search CSS cascade
trick, and the four Pagefind 1.5.2 quirks the UI works around
(stretched form, no-op Enter, empty thumbnail, UA-yellow marks)
- Every Makefile target with what it does and the gotchas (serve does
not rebuild; serve-lan is plain static; build runs --gc)
- Search usage incl. the new keyboard navigation, and a table of the
current #search customization values
- Dead-link automation (internal/external split, idempotent
comment-out, daily cron job)
- Troubleshooting for the new failure modes and a do-not-commit list
This commit is contained in:
@@ -1,90 +1,431 @@
|
|||||||
# Building The Website
|
# Liberty Tools (libertytools.io) — Hugo static site
|
||||||
## Clone Repository
|
|
||||||
`git clone https://dev.libertytools.io/adminuser/libertytoolsio.git`
|
A Hugo site built on the [hugo-book](https://github.com/alex-shpak/hugo-book) theme,
|
||||||
|
with **Pagefind** client-side search and **lychee**-based dead-link checking. This
|
||||||
|
document is the single reference for understanding the architecture, building,
|
||||||
|
previewing, searching, and maintaining the site.
|
||||||
|
|
||||||
|
> If you only need to build and preview, jump to
|
||||||
|
> [Build & preview (§4)](#4-build--preview-make-commands). Everything you normally
|
||||||
|
> type is a `make` target.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of contents
|
||||||
|
|
||||||
|
- [Architecture](#architecture)
|
||||||
|
- [1. Clone](#1-clone)
|
||||||
|
- [2. Prerequisites (install once)](#2-prerequisites-install-once)
|
||||||
|
- [3. Configuration](#3-configuration)
|
||||||
|
- [4. Build & preview (make commands)](#4-build--preview-make-commands)
|
||||||
|
- [5. Search (Pagefind)](#5-search-pagefind)
|
||||||
|
- [6. Dead links](#6-dead-links)
|
||||||
|
- [7. Troubleshooting](#7-troubleshooting)
|
||||||
|
- [8. Do not commit](#8-do-not-commit)
|
||||||
|
- [Repository layout](#repository-layout)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The site is a **fully static** output: no server-side rendering at request time, no
|
||||||
|
database, no runtime. Hugo renders the markdown to plain HTML files *at build time*;
|
||||||
|
a search index is generated from those files; and a trivial static file server
|
||||||
|
(delivering the built directory) is all that runs in front of a browser.
|
||||||
|
|
||||||
|
### The three moving parts
|
||||||
|
|
||||||
|
| Component | Role |
|
||||||
|
|--------------|---------------------------------------------------------------------------------------|
|
||||||
|
| **Hugo** | The static site generator. Reads `content/*.md` + the theme, emits `public/`. |
|
||||||
|
| **hugo-book** | The theme (vendored under `themes/hugo-book/`). Layouts, SCSS styling, menu/TOC. |
|
||||||
|
| **Pagefind** | Client-side full-text search. Indexes the built `public/` into `public/pagefind/`. |
|
||||||
|
| **lychee** | Dead-link checker (used only by the `link-check`/`link-fix`/`external-check` targets).|
|
||||||
|
|
||||||
|
Only Hugo and Node (for Pagefind's `npx` wrapper) are required to build. `lychee` is
|
||||||
|
needed only if you run the dead-link targets.
|
||||||
|
|
||||||
|
### Content → build → serve, data flow
|
||||||
|
|
||||||
## Run Makefile
|
|
||||||
The `Makefile` in the root of the directory will run the following two commands:
|
|
||||||
``` bash
|
|
||||||
hugo server &
|
|
||||||
npx -y pagefind --site public --serve
|
|
||||||
```
|
```
|
||||||
The first command will run and compile the `hugo` website and any changes made. The second command will build and re-index the search bar index.
|
┌──────────────────────────────────────────────┐
|
||||||
|
│ content/ │
|
||||||
To build the website, simply run
|
│ *.md pages + images (the source) │
|
||||||
``` bash
|
└──────────────────────┬───────────────────────┘
|
||||||
make run
|
│ make build (hugo --gc)
|
||||||
```
|
▼
|
||||||
in the project root to run those two commands found in the `Makefile`.
|
┌──────────────────────────────────────────────┐
|
||||||
|
│ public/ │
|
||||||
|
│ rendered static HTML + fingerprinted CSS/JS │
|
||||||
# Viewing Changes to Website Locally
|
│ (hugo-book SCSS compiled by the extended │
|
||||||
After running the `Makefile` you should get an output like this"
|
│ build; each asset hash-named + SRI-checked)│
|
||||||
|
└──────────────────────┬───────────────────────┘
|
||||||
``` shell
|
│ make index (pagefind --site public)
|
||||||
hugo server &
|
▼
|
||||||
npx -y pagefind --site public --serve
|
┌──────────────────────────────────────────────┐
|
||||||
Watching for changes in /path/to/libertytools_sites/dev_lt/libertytoolsio/{archetypes,content,data,layouts,static,themes}
|
│ public/pagefind/ │
|
||||||
Watching for config changes in /path/to/libertytools_sites/dev_lt/libertytoolsio/config.toml, /path/to/libertytools_sites/dev_lt/libertytoolsio/themes/hugo-book/hugo.toml
|
│ Pagefind search index (WASM + JSON) │
|
||||||
Start building sites …
|
│ — ships inside public/, so any static │
|
||||||
hugo v0.151.0-c70ab27ceb841fc9404eab5d2c985ff7595034b7+extended linux/amd64 BuildDate=2025-10-02T13:30:36Z VendorInfo=gohugoio
|
│ server can serve a working search │
|
||||||
|
└──────────────────────┬───────────────────────┘
|
||||||
|
│ make serve / make serve-lan
|
||||||
│ EN
|
▼
|
||||||
──────────────────┼─────
|
browser (any machine on the network)
|
||||||
Pages │ 27
|
|
||||||
Paginator pages │ 0
|
|
||||||
Non-page files │ 65
|
|
||||||
Static files │ 106
|
|
||||||
Processed images │ 0
|
|
||||||
Aliases │ 0
|
|
||||||
Cleaned │ 0
|
|
||||||
|
|
||||||
Built in 100 ms
|
|
||||||
Environment: "development"
|
|
||||||
Serving pages from disk
|
|
||||||
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
|
|
||||||
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
|
|
||||||
Press Ctrl+C to stop
|
|
||||||
|
|
||||||
Running Pagefind v1.5.2 (Extended)
|
|
||||||
Running from: "/path/to/libertytools_sites/dev_lt/libertytoolsio"
|
|
||||||
Source: "public"
|
|
||||||
Output: "public/pagefind"
|
|
||||||
|
|
||||||
[Walking source directory]
|
|
||||||
Found 50 files matching **/*.{html}
|
|
||||||
|
|
||||||
[Parsing files]
|
|
||||||
Did not find a data-pagefind-body element on the site.
|
|
||||||
↳ Indexing all <body> elements on the site.
|
|
||||||
|
|
||||||
[Reading languages]
|
|
||||||
Discovered 2 languages: en-us, en
|
|
||||||
|
|
||||||
[Building search indexes]
|
|
||||||
Total:
|
|
||||||
Indexed 2 languages
|
|
||||||
Indexed 48 pages
|
|
||||||
Indexed 6934 words
|
|
||||||
Indexed 0 filters
|
|
||||||
Indexed 0 sorts
|
|
||||||
|
|
||||||
┌─────────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ Pagefind found references to the Default UI (pagefind-ui.js) │
|
|
||||||
│ on your site. The Default UI is supported and will continue │
|
|
||||||
│ to work. │
|
|
||||||
│ │
|
|
||||||
│ As of 1.5.0, if you are setting up a new integration, use the │
|
|
||||||
│ Component UI instead. It includes a search modal, better │
|
|
||||||
│ accessibility and customization: https://pagefind.app/docs/search-ui/ │
|
|
||||||
└─────────────────────────────────────────────────────────────────────────┘
|
|
||||||
|
|
||||||
Finished in 0.067 seconds
|
|
||||||
|
|
||||||
Serving the Pagefind Playground at http://localhost:1414/pagefind/playground/
|
|
||||||
Serving "public" at http://localhost:1414
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Copy `http://localhost:1414` into your web browser and you should then see the website as it is with all the changes made, but only served from your local machine. This version of the site is not public facing.
|
Two consequences worth internalizing:
|
||||||
|
|
||||||
|
1. **`public/` is the single source of truth for what is served.** Both the search
|
||||||
|
index and every preview server read from it. If the site "isn't showing your
|
||||||
|
change," the build hasn't run — re-run `make index`.
|
||||||
|
2. **The Pagefind bundle lives inside `public/`.** That's why `make serve-lan` (a plain
|
||||||
|
Python static server) serves working search with zero extra setup — there is no
|
||||||
|
special search server required.
|
||||||
|
|
||||||
|
### The theme SCSS pipeline
|
||||||
|
|
||||||
|
`html-head.html` compiles the theme's stylesheet on every build:
|
||||||
|
|
||||||
|
```
|
||||||
|
book.scss → resources.ExecuteAsTemplate → css.Sass → resources.Minify → resources.Fingerprint
|
||||||
|
```
|
||||||
|
|
||||||
|
`css.Sass` is why the **Hugo *extended*** edition is mandatory (the standard build has
|
||||||
|
no SCSS compiler). `resources.Fingerprint` hashes the output into the filename
|
||||||
|
(`book.min.<sha>.css`) and embeds an SRI `integrity` attribute on the `<link>`; a
|
||||||
|
single byte change in the CSS produces a new filename, which is why old cached
|
||||||
|
stylesheets never collide with new ones.
|
||||||
|
|
||||||
|
### Where your customizations live
|
||||||
|
|
||||||
|
Two files hold all site-level customization — you rarely need to touch anything else:
|
||||||
|
|
||||||
|
- **`themes/hugo-book/layouts/_partials/docs/search.html`** — the entire search UI:
|
||||||
|
the Pagefind `new PagefindUI({...})` config, the `#search`-scoped `<style>` block
|
||||||
|
that fits the UI to the sidebar, and the keyboard-navigation script. (See §5.)
|
||||||
|
- **`themes/hugo-book/assets/_variables.scss`** — site-wide dimensions and font.
|
||||||
|
This file is imported **after** the theme's `_defaults.scss`, so the values here
|
||||||
|
*override* the theme defaults with plain (non-`!default`) assignments.
|
||||||
|
|
||||||
|
| Variable | Current value | Effect |
|
||||||
|
|---------------------------|---------------|-----------------------------------------------------|
|
||||||
|
| `:root { --font-size }` | `17px` | Site-wide base font (theme default is 16px). All `rem` sizes scale off this. |
|
||||||
|
| `$container-max-width` | `100rem` | Max width of the centered content area (~1700px at 17px root). |
|
||||||
|
| `$menu-width` | `18.75rem` | Width of the **left sidebar** — the category list *and* the search box *and* the search-results pane are all this one column. |
|
||||||
|
| `$mobile-breakpoint` | *(recomputed)*| **Must be recomputed whenever you change `$menu-width`/`$body-min-width`/`$toc-width`** — it is derived as `$menu-width + $body-min-width * 1.2 + $toc-width`. |
|
||||||
|
|
||||||
|
### Search index scoping (`data-pagefind-body`)
|
||||||
|
|
||||||
|
By default Pagefind indexes the *entire* `<body>` of each page — including the
|
||||||
|
sidebar, menu, and footer — which pollutes results. Instead, the article element in
|
||||||
|
each layout is tagged with `data-pagefind-body`:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<article class="markdown book-article" data-pagefind-body>
|
||||||
|
```
|
||||||
|
|
||||||
|
Pagefind then indexes **only** that element and *skips any page that lacks the
|
||||||
|
attribute*. The attribute is present in `layouts/baseof.html` (default single + home),
|
||||||
|
`layouts/term.html` (list pages), and `layouts/posts/{single,list}.html`.
|
||||||
|
`404.html` is intentionally left untagged (it has its own standalone `<html>`).
|
||||||
|
**Do not remove these attributes** or the menu text will start appearing in search
|
||||||
|
results. After any change, confirm the indexed page count in the `make index` log —
|
||||||
|
a sharp drop means a page lost its tag (usually the home page).
|
||||||
|
|
||||||
|
### The `#search` CSS cascade trick
|
||||||
|
|
||||||
|
`pagefind-ui.css` is loaded in `<body>` (by the search partial) — **after** the theme's
|
||||||
|
head stylesheet. A CSS rule in the head at equal specificity therefore *loses* the
|
||||||
|
cascade. All search overrides live in a `#search`-scoped `<style>` block in
|
||||||
|
`search.html`; the `#search` id + class gives them higher specificity than
|
||||||
|
Pagefind's class-only rules regardless of load order. Keep this pattern when you tune
|
||||||
|
the search UI.
|
||||||
|
|
||||||
|
### Pagefind 1.5.2 quirks we work around (all in `search.html`)
|
||||||
|
|
||||||
|
These are non-obvious behaviors of the Default UI in Pagefind **1.5.2** (the version
|
||||||
|
`npx -y pagefind` resolves to). They cost real debugging time; they are documented
|
||||||
|
here so the next person doesn't rediscover them:
|
||||||
|
|
||||||
|
- **The results drawer is an in-flow child of the form.** Once results render,
|
||||||
|
`.pagefind-ui__form` stretches to the full height of the results list (measured
|
||||||
|
~1800px for a dozen rows). The fixed-height sidebar (`.book-menu-content`,
|
||||||
|
`position:fixed; overflow-y:auto`) simply scrolls it — that's *intended*. Consequence:
|
||||||
|
any `%`-based height on the absolutely-positioned clear button would resolve against
|
||||||
|
that stretched form, so the clear button uses **fixed** pixel dimensions keyed to the
|
||||||
|
input.
|
||||||
|
- **There is no keyboard navigation, and stock Enter is a silent no-op.** The built
|
||||||
|
`pagefind-ui.js` handles `Enter` with only `preventDefault()` — typing a query and
|
||||||
|
pressing Enter navigates nowhere. A custom **capture-phase** `keydown` listener on
|
||||||
|
`#search` implements ↑/↓ highlighting and Enter-to-open (see §5).
|
||||||
|
- **An empty thumbnail occupies the left of every row.** Even with zero images in the
|
||||||
|
index, Pagefind renders an ~81px `.pagefind-ui__result-thumb` placeholder, which
|
||||||
|
pushes result text ~90px right (a "right-justified" look). We hide it
|
||||||
|
(`display:none`) because this index has no images.
|
||||||
|
- **Matched terms render in harsh UA-yellow.** Pagefind's CSS reset does
|
||||||
|
`mark { all: revert }`, so `<mark>` falls back to the browser default (yellow). We
|
||||||
|
restyle `#search .pagefind-ui__result mark` to a subdued slate.
|
||||||
|
|
||||||
|
### Dead-link automation
|
||||||
|
|
||||||
|
Two independent checks, deliberately split by scope and cost:
|
||||||
|
|
||||||
|
- **Internal** (`make link-check` / `make link-fix`): *offline*. `lychee` checks the
|
||||||
|
source markdown against the built site (`--root-dir public`, `--scheme file`), so no
|
||||||
|
network is involved. Confirmed-dead *internal page* links are auto-commented out by
|
||||||
|
`scripts/fix_dead_links.py`; external/bare-domain/empty links are routed to a manual
|
||||||
|
review file, never auto-edited.
|
||||||
|
- **External** (`make external-check`): *network*. `scripts/check_external_links.py`
|
||||||
|
pings every `http(s)` link in `content/`. Report-only, always exits 0. A daily cron
|
||||||
|
job runs it (see §6).
|
||||||
|
|
||||||
|
The removal mechanism for internal links is **commenting, not deleting**
|
||||||
|
(`[Old](/gone/)` → `<!-- [Old](/gone/) -->`). An HTML comment survives the Hugo build
|
||||||
|
verbatim, is invisible on the rendered page, is excluded from the search index, and is
|
||||||
|
no longer detected by `lychee` — which makes `make link-fix` **idempotent** (safe to
|
||||||
|
re-run; it never loops).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Clone
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://dev.libertytools.io/adminuser/libertytoolsio.git
|
||||||
|
cd libertytoolsio
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Prerequisites (install once)
|
||||||
|
|
||||||
|
All tools install into `~/.local/bin` (no sudo). Make sure `~/.local/bin` is on your
|
||||||
|
`PATH` (it usually is; if not: `export PATH="$HOME/.local/bin:$PATH"`).
|
||||||
|
|
||||||
|
- **Hugo extended (v0.167.0 or newer).** The theme compiles SCSS with `css.Sass`, so
|
||||||
|
the *extended* build is required. From the repo root:
|
||||||
|
```
|
||||||
|
cd "$(mktemp -d)"
|
||||||
|
curl -sL -o h.tgz https://github.com/gohugoio/hugo/releases/download/v0.167.0/hugo_extended_0.167.0_linux-amd64.tar.gz
|
||||||
|
echo "0163f5c3deddac1f494a1629ddc40c65d18de9d5794facd98f7f96ac2c7d8957 h.tgz" | sha256sum -c -
|
||||||
|
tar xzf h.tgz
|
||||||
|
install -m 0755 hugo "$HOME/.local/bin/hugo"
|
||||||
|
hugo version # must print ...+extended...
|
||||||
|
```
|
||||||
|
- **Node (v18+).** Pagefind runs through `npx -y pagefind`, which auto-installs the
|
||||||
|
Pagefind CLI the first time (resolves to **1.5.x**; the UI quirks in
|
||||||
|
[Architecture](#pagefind-152-quirks-we-work-around-all-in-searchhtml) are for 1.5.2).
|
||||||
|
Verify: `node --version && npx --version`.
|
||||||
|
- **lychee (v0.24.2)** — link checker, needed only for the dead-link targets:
|
||||||
|
```
|
||||||
|
cd "$(mktemp -d)"
|
||||||
|
curl -sL -o ly.tgz https://github.com/lycheeverse/lychee/releases/download/lychee-v0.24.2/lychee-x86_64-unknown-linux-musl.tar.gz
|
||||||
|
tar xzf ly.tgz
|
||||||
|
install -m 0755 lychee-x86_64-unknown-linux-musl/lychee "$HOME/.local/bin/lychee"
|
||||||
|
lychee --version # lychee 0.24.2
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Configuration
|
||||||
|
|
||||||
|
- **`config.toml`** — site-level settings: `theme = "hugo-book"`, `BookSearch = true`,
|
||||||
|
`BookTheme = 'dark'`, `plausibleDomain`, and `markup.goldmark.renderer.unsafe = true`
|
||||||
|
(required so raw HTML — including the commented-out dead links and inline `<style>`
|
||||||
|
blocks — passes through the markdown renderer unharmed).
|
||||||
|
- **`themes/hugo-book/assets/_variables.scss`** — site dimensions + font (see the
|
||||||
|
table in [Architecture](#where-your-customizations-live)). This is the file to edit
|
||||||
|
to change the base font size, page width, or sidebar width.
|
||||||
|
- **`themes/hugo-book/layouts/_partials/docs/search.html`** — the search UI (see §5).
|
||||||
|
|
||||||
|
## 4. Build & preview (make commands)
|
||||||
|
|
||||||
|
Run from the project root. `public/`, `linkcheck/`, and `resources/_gen/` are
|
||||||
|
generated and gitignored.
|
||||||
|
|
||||||
|
| Target | What it does |
|
||||||
|
|-----------------------|--------------|
|
||||||
|
| `make build` | Build Hugo content into `public/` (`hugo --gc`). |
|
||||||
|
| `make index` | `build` + build the Pagefind search index into `public/pagefind/`. **This is the normal "I edited content, re-make it" command.** |
|
||||||
|
| `make serve` / `make run` | `index` + serve the built site **with search** at `http://localhost:1414/` (local only). Uses Pagefind's own server, which binds 127.0.0.1. |
|
||||||
|
| `make serve-lan` | Serve the built site on **all interfaces** so another machine on the network can open it: `http://<LAN-IP>:8090/`. Python stdlib only; build first with `make index`. |
|
||||||
|
| `make link-check` | **Offline** scan of *internal* links in `content/` → `linkcheck/internal.json`. Fast, no network. |
|
||||||
|
| `make link-fix` | Comment out confirmed dead *internal* links, then rebuild content + index. |
|
||||||
|
| `make external-check` | **Network** scan of all *external* links in `content/` → console report + `linkcheck/external_dead.txt`. Report-only; always exits 0. |
|
||||||
|
| `make clean` | Remove `public/` and `linkcheck/`. |
|
||||||
|
| `make all` | `build` + `index` (the default target). |
|
||||||
|
|
||||||
|
Notes on the targets:
|
||||||
|
|
||||||
|
- **`build` runs `hugo --gc`** — `--gc` garbage-collects orphaned resources from the
|
||||||
|
asset cache.
|
||||||
|
- **`serve` / `serve-lan` do not rebuild.** They serve whatever is already in
|
||||||
|
`public/`. Always run `make index` first (or use `make serve`, which chains `index`),
|
||||||
|
or you'll be serving a stale build.
|
||||||
|
- **Why `serve` uses `pagefind --serve`, not `hugo server`:** `hugo server` renders in
|
||||||
|
memory and may not write `public/`, leaving Pagefind with nothing to index.
|
||||||
|
Serving the *built* `public/` through Pagefind's own static server is what makes the
|
||||||
|
preview match production.
|
||||||
|
- **`serve-lan` is a plain static file server** (`scripts/serve_lan.py`, stdlib only)
|
||||||
|
rooted at `public/` on `0.0.0.0:8090`. It exists specifically because `make serve`
|
||||||
|
binds to 127.0.0.1 and is unreachable from other machines.
|
||||||
|
|
||||||
|
### Viewing changes locally
|
||||||
|
|
||||||
|
```
|
||||||
|
make serve
|
||||||
|
```
|
||||||
|
Open `http://localhost:1414/`. There is no live reload — re-run `make serve` after
|
||||||
|
editing content.
|
||||||
|
|
||||||
|
### Viewing from another machine on the same network
|
||||||
|
|
||||||
|
```
|
||||||
|
make index # build first so public/pagefind/ exists
|
||||||
|
make serve-lan
|
||||||
|
```
|
||||||
|
`serve-lan` prints this machine's LAN IP at startup (also: `hostname -I | head -1`).
|
||||||
|
On the **other** machine, open `http://<that-printed-IP>:8090/`. The IP is dynamic
|
||||||
|
(DHCP); re-check it if it changes. Search works identically under this plain static
|
||||||
|
server as under `make serve` (the Pagefind bundle is part of `public/`, so no special
|
||||||
|
server is needed for cross-machine access).
|
||||||
|
|
||||||
|
## 5. Search (Pagefind)
|
||||||
|
|
||||||
|
The search box lives in the **left sidebar** on every page (injected via
|
||||||
|
`themes/hugo-book/layouts/_partials/docs/menu.html` → `docs/search.html`).
|
||||||
|
|
||||||
|
### Using it
|
||||||
|
|
||||||
|
- Press **`/`** anywhere on a page to jump to the search box (`focusOnSlash`).
|
||||||
|
- Type a query; results appear below the box, scoped to page content (see
|
||||||
|
`data-pagefind-body` in [Architecture](#search-index-scoping-data-pagefind-body)).
|
||||||
|
- **Navigate with the keyboard:**
|
||||||
|
- **`↓` / `↑`** move a highlight through the results, in visual order. Top-level
|
||||||
|
hits *and* their sub-topics (Pagefind "sub-results") each get their own highlight —
|
||||||
|
e.g. a query under "Hacking" can step through Hacking, then its Anonymity / OSINT /
|
||||||
|
Security Courses sub-entries, one at a time. Wraps around at the ends.
|
||||||
|
- **`Enter`** opens the highlighted result. If nothing is highlighted, it opens the
|
||||||
|
top result. (Stock Pagefind does neither — this is the custom listener described in
|
||||||
|
[Architecture](#pagefind-152-quirks-we-work-around-all-in-searchhtml).)
|
||||||
|
- The matched terms inside each result are shown in a subdued slate (not the browser's
|
||||||
|
default yellow).
|
||||||
|
|
||||||
|
### Customizing the look
|
||||||
|
|
||||||
|
Everything is in the `#search`-scoped `<style>` block and the `new PagefindUI({...})`
|
||||||
|
call in `themes/hugo-book/layouts/_partials/docs/search.html`. **Always keep the
|
||||||
|
`#search` prefix** on overrides — see the [cascade trick](#the-search-css-cascade-trick).
|
||||||
|
Current values (verified against the built page):
|
||||||
|
|
||||||
|
| Element | Current value |
|
||||||
|
|--------------------------------------|---------------|
|
||||||
|
| Input font / height / padding | `font-size: 0.85em; height: 2.4em; min-height: 2.4em; padding: 0 2.6em 0 2.4em;` |
|
||||||
|
| Magnifier icon (`.pagefind-ui__form:before`) | `top: 7px; left: 12px; width/height: 14px;` — fades to `opacity: 0` while the input has text |
|
||||||
|
| Clear button (`.pagefind-ui__search-clear`) | `top: 3px; right: 3px; height: 23px; font-size: 11px; padding: 0 6px;` (fixed px, *not* `%` — see form-stretch quirk) |
|
||||||
|
| Results layout (`.pagefind-ui__drawer`) | `flex-direction: column; gap: 0.4em;` (stack, don't wrap) |
|
||||||
|
| Shrink guards (`.results-area`, `.result-excerpt`) | `min-width: 0;` |
|
||||||
|
| Empty thumbnail (`.result-thumb`) | `display: none;` (no images in this index) |
|
||||||
|
| Result title / link | `font-size: 1em; overflow-wrap: anywhere;` |
|
||||||
|
| Result excerpt | `font-size: 0.88em; overflow-wrap: anywhere; word-break: break-word;` |
|
||||||
|
| "N results for …" message | `font-size: 0.8em;` |
|
||||||
|
| Keyboard-selected row (`.pf-kbd-selected`) | `background: rgba(100,116,139,0.5); outline: 2px solid #cbd5e1; outline-offset: -2px; color: #fff;` |
|
||||||
|
| Matched term (`mark`) | `background: rgba(100,116,139,0.45); color: #fff; font-weight: 600; border-radius: 2px;` |
|
||||||
|
| Excerpt length (words) | `excerptLength: 20` in the `new PagefindUI({...})` call |
|
||||||
|
|
||||||
|
The legacy Fuse.js search was **removed** (it double-loaded with Pagefind). A backup of
|
||||||
|
the old head partial is at `themes/hugo-book/layouts/_partials/docs/html-head.html.bkup`.
|
||||||
|
|
||||||
|
## 6. Dead links
|
||||||
|
|
||||||
|
### Internal links (fast, offline)
|
||||||
|
|
||||||
|
```
|
||||||
|
make link-check # writes linkcheck/internal.json
|
||||||
|
make link-fix # comments out confirmed dead internal page links, then rebuilds
|
||||||
|
```
|
||||||
|
|
||||||
|
How it works: `lychee` checks the source markdown against the built site
|
||||||
|
(`--root-dir public`). A link whose target page does not exist is a *dead internal
|
||||||
|
link* and `scripts/fix_dead_links.py` wraps it in an HTML comment:
|
||||||
|
`[Old](/gone/)` → `<!-- [Old](/gone/) -->`. A commented link disappears from both the
|
||||||
|
rendered page and the search index, and is **not** re-detected, so `make link-fix` is
|
||||||
|
idempotent and safe to run repeatedly.
|
||||||
|
|
||||||
|
**Safety rule:** only *internal page* links are auto-commented. External `https://`
|
||||||
|
failures, bare-domain links, and empty `href`s are written to
|
||||||
|
`linkcheck/manual_review.txt` and left for you to fix by hand (a bare domain may be a
|
||||||
|
typo to fix, not a link to delete).
|
||||||
|
|
||||||
|
### External links (network, daily)
|
||||||
|
|
||||||
|
```
|
||||||
|
make external-check # pings every http/https link in content; report-only
|
||||||
|
```
|
||||||
|
|
||||||
|
It lists each dead external link with its `file:line` and writes
|
||||||
|
`linkcheck/external_dead.txt`. It does **not** edit anything. A scheduled job runs this
|
||||||
|
once a day (below).
|
||||||
|
|
||||||
|
### The daily external-link job
|
||||||
|
|
||||||
|
A cron job named **`libertytools-external-link-check`** runs
|
||||||
|
`scripts/check_external_links.py` **every day at 6:00am**. It is report-only and always
|
||||||
|
exits 0. Its output is saved and viewable with:
|
||||||
|
|
||||||
|
```
|
||||||
|
hermes cronjob list # confirm it exists and is enabled
|
||||||
|
```
|
||||||
|
- To run it manually right now: `hermes cronjob run <job_id>` (from `hermes cronjob list`).
|
||||||
|
- To pause it: `hermes cronjob pause <job_id>`; to resume: `hermes cronjob resume <job_id>`.
|
||||||
|
- To change the time, edit the job's schedule in `config.yaml` (or ask the agent).
|
||||||
|
|
||||||
|
## 7. Troubleshooting
|
||||||
|
|
||||||
|
- **`hugo: command not found`** → you're using the non-extended build or it isn't on
|
||||||
|
PATH. Re-run the Hugo install above and confirm `hugo version` says `+extended`.
|
||||||
|
- **Search shows no results** → the index wasn't built. Run `make index`, confirm the
|
||||||
|
log prints `Indexed N pages` and `public/pagefind/pagefind-ui.js` exists, then reload.
|
||||||
|
If the page count looks low, a layout lost its `data-pagefind-body` attribute.
|
||||||
|
- **`pagefind` / `lychee` not found** → re-run the relevant install in §2; both live in
|
||||||
|
`~/.local/bin`.
|
||||||
|
- **`make serve` says port 1414 in use** → a previous server is still running. Stop it
|
||||||
|
(Ctrl+C in its terminal) or find it: `ss -ltnp | grep 1414`.
|
||||||
|
- **Other machine can't reach `http://<LAN-IP>:8090/`** → confirm you're on the same
|
||||||
|
network and using the IP `serve-lan` printed (`hostname -I | head -1` on the host).
|
||||||
|
The host's firewall may need to allow inbound 8090.
|
||||||
|
- **Search is too big/small for the sidebar** → tune the `#search` block in
|
||||||
|
`themes/hugo-book/layouts/_partials/docs/search.html` (see §5).
|
||||||
|
- **Search results look right-justified / have a big left gap** → the empty thumbnail
|
||||||
|
isn't hidden; ensure `#search .pagefind-ui__result-thumb { display: none; }` is
|
||||||
|
present (see the quirk in [Architecture](#pagefind-152-quirks-we-work-around-all-in-searchhtml)).
|
||||||
|
- **Up/down arrows don't highlight results** → the keyboard-nav script in
|
||||||
|
`search.html` is missing or its `rowFor()` no longer matches the result DOM. Sub-results
|
||||||
|
are nested inside the top-level `<li>` (see the quirk notes).
|
||||||
|
|
||||||
|
## 8. Do not commit
|
||||||
|
|
||||||
|
All changes from a maintenance pass are left in the working tree for review. Commit
|
||||||
|
**only after you have validated the result** (`make build` exits 0 and you've checked
|
||||||
|
the rendered output). The following are generated and gitignored — never commit them:
|
||||||
|
|
||||||
|
- `public/` — built site output
|
||||||
|
- `linkcheck/` — link-check reports
|
||||||
|
- `resources/_gen/` — Hugo asset-generation cache
|
||||||
|
- `.hermes/` — agent working files
|
||||||
|
|
||||||
|
## Repository layout
|
||||||
|
|
||||||
|
```
|
||||||
|
content/ # site pages (markdown + images)
|
||||||
|
themes/hugo-book/ # the theme (vendored). Do not edit themes/hugo-book.backup/.
|
||||||
|
assets/_variables.scss # SITE overrides: font, page width, sidebar width, breakpoint
|
||||||
|
layouts/baseof.html # root layout; <article data-pagefind-body>
|
||||||
|
layouts/term.html # list/term pages (data-pagefind-body)
|
||||||
|
layouts/posts/{single,list}.html # blog post pages (data-pagefind-body)
|
||||||
|
layouts/_partials/docs/search.html # Pagefind UI + sidebar-fit CSS + keyboard nav
|
||||||
|
layouts/_partials/docs/menu.html # injects search.html into the sidebar
|
||||||
|
layouts/_partials/docs/html-head.html # (legacy fuse search removed; .bkup kept)
|
||||||
|
layouts/_partials/docs/html-head.html.bkup # backup of the pre-Pagefind head
|
||||||
|
scripts/
|
||||||
|
fix_dead_links.py # internal dead-link comment-out (idempotent)
|
||||||
|
check_external_links.py # external dead-link report (report-only, daily cron)
|
||||||
|
serve_lan.py # static LAN preview server (serves public/ on 0.0.0.0:8090)
|
||||||
|
Makefile # build / index / serve / run / serve-lan / link-check / link-fix / external-check / clean
|
||||||
|
config.toml # theme + BookSearch + BookTheme + goldmark.unsafe
|
||||||
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user