#!/usr/bin/env bash # The manual. # # docs/ in this repository is engineering artifacts: design specs, plans, an # upstream ledger. None of it is written for the person using the desktop, and # that person is the one with questions. The manual is the answer, and it is # rendered inside Settings so a chapter can point at a settings page and have # that mean something. # # What must hold: # # 1. Every chapter the page lists exists, and every chapter file is listed. # A renamed file shows an error card in place of a chapter, which looks # like the manual is broken rather than like somebody moved a file. # 2. Chapters render one at a time. Text has an implicit texture size limit, # and a document long enough to hit it goes blank rather than complaining. # 3. Links leave the desktop rather than doing nothing. # 4. The page is registered everywhere a settings page has to be, or it # silently redirects to Home. set -uo pipefail repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" manual_dir="$repo_dir/config/dot/quickshell/manual" page="$repo_dir/config/dot/quickshell/modules/settings/ManualPage.qml" sidebar="$repo_dir/config/dot/quickshell/modules/settings/SettingsSidebar.qml" shell_ui="$repo_dir/config/dot/quickshell/modules/settings/SettingsShell.qml" qmldir="$repo_dir/config/dot/quickshell/modules/settings/qmldir" state="$repo_dir/config/dot/quickshell/services/ShellState.qml" search="$repo_dir/config/dot/quickshell/services/SettingsSearch.qml" findings=() note() { findings+=("$1"); } [[ -d "$manual_dir" ]] || { printf 'manual contract: %s is missing\n' "$manual_dir" >&2; exit 1; } # ── 1. The chapters on disk and the chapters listed are the same set ───────── mapfile -t on_disk < <(find "$manual_dir" -maxdepth 1 -name '*.md' -printf '%f\n' | sort) (( ${#on_disk[@]} > 0 )) || note 'the manual has no chapters' mapfile -t listed < <(grep -oE 'file: "[^"]+\.md"' "$page" | sed 's/file: "//; s/"//' | sort) (( ${#listed[@]} > 0 )) || note 'the manual page lists no chapters' for file in "${on_disk[@]}"; do printf '%s\n' "${listed[@]}" | grep -qx "$file" \ || note "$file exists but the manual page never shows it" done for file in "${listed[@]}"; do [[ -r "$manual_dir/$file" ]] \ || note "the manual page lists $file, which does not exist, so that chapter renders an error" done # A chapter that is only a heading is a chapter somebody forgot to write. for file in "${on_disk[@]}"; do lines="$(grep -c . "$manual_dir/$file" || true)" (( lines > 10 )) || note "$file has $lines lines; it reads as unfinished" head -1 "$manual_dir/$file" | grep -q '^# ' \ || note "$file does not begin with a heading, so it has no title of its own" done # ── 2 & 3. How it renders ──────────────────────────────────────────────────── grep -q 'textFormat: Text.MarkdownText' "$page" \ || note 'chapters are not rendered as markdown, so the source appears verbatim' grep -q 'root.chapters\[root.current\]' "$page" \ || note 'the page does not render one chapter at a time; a single long Text goes blank rather than erroring' grep -q 'onLinkActivated' "$page" \ || note 'links in the manual do nothing when clicked' grep -q 'onLoadFailed' "$page" \ || note 'a chapter that cannot be read fails silently instead of saying so' # The chapters are reached through the shell directory, not by walking upward # out of it: that path is only correct when the repository is where it usually # is, and the shell directory is a symlink. grep -q 'Quickshell.shellDir + "/manual/"' "$page" \ || note 'the manual is not located through the shell directory, so it would break on a clone elsewhere' grep -q '\.\./\.\./\.\.' "$page" \ && note 'the manual path walks upward out of the shell directory, which is only correct by accident' # ── 4. Registered in all five places ───────────────────────────────────────── grep -q '{ page: "manual"' "$sidebar" || note 'the manual has no sidebar entry' grep -q 'case "manual": return manualPage;' "$shell_ui" || note 'SettingsShell does not route to the manual' grep -q 'Component { id: manualPage; ManualPage {} }' "$shell_ui" || note 'SettingsShell never declares the manual component' grep -q '^ManualPage 1.0 ManualPage.qml$' "$qmldir" || note 'ManualPage is not registered in the settings qmldir' grep -q '"manual"' "$state" || note 'the manual is not in the allowed settings pages, so openSettings would redirect to Home' grep -q 'page: "manual"' "$search" || note 'the manual is not searchable from the settings search box' if (( ${#findings[@]} > 0 )); then printf 'manual contract: %d finding(s)\n' "${#findings[@]}" >&2 printf ' - %s\n' "${findings[@]}" >&2 exit 1 fi printf 'manual contract: PASS (%d chapters)\n' "${#on_disk[@]}"