Files
Panama/config/dot/quickshell/modules/settings/ManualPage.qml
T

113 lines
4.2 KiB
QML

// The manual.
//
// Panama's docs/ directory is engineering artifacts: design specs, plans, an
// upstream ledger. None of it is written for the person using the desktop,
// and the person using the desktop is the one with questions.
//
// So the manual is prose, in chapters, rendered here rather than opened in a
// browser. Keeping it in Settings means it is reachable from the same place
// as everything else it talks about, and a link to a settings page can just
// be a settings page -- see onLinkActivated below, where that stopped being a
// figure of speech.
//
// The chapter list and the chapter titles both come from ManualChapters, which
// reads each file's own first heading. This page used to carry a hand-written
// copy of those titles under a comment claiming they were read from the files.
//
// One Text per chapter, never the whole manual at once: Text has an implicit
// texture size limit, and a document long enough to hit it fails by going
// blank rather than by complaining.
import Quickshell
import Quickshell.Io
import QtQuick
import qs.config
import qs.services
SettingsPage {
id: root
title: "Manual"
lede: "How this desktop works, for the person using it."
readonly property var chapters: contents.titled
property int current: 0
// A deep link picks the chapter once and then lets go, so arriving at a
// chapter does not pin you to it. Same consume-once idiom ShellState uses
// for settings sections.
Component.onCompleted: {
const section = ShellState.takeSettingsSection();
for (let index = 0; index < root.chapters.length; index++) {
if (root.chapters[index].file.indexOf(section) >= 0 && section !== "") {
root.current = index;
break;
}
}
}
ManualChapters { id: contents }
SettingsTabs {
tabs: root.chapters.map((chapter, index) => ({
value: String(index),
label: chapter.title
}))
current: String(root.current)
onSelected: value => root.current = parseInt(value, 10)
}
SettingsCard {
FileView {
id: chapterFile
path: Quickshell.shellDir + "/manual/" + root.chapters[root.current].file
printErrors: false
onLoaded: body.text = this.text()
onLoadFailed: body.text =
"This chapter could not be read.\n\nIt should be at `"
+ chapterFile.path + "`."
}
Text {
id: body
width: parent.width
textFormat: Text.MarkdownText
wrapMode: Text.WordWrap
color: Theme.fgDim
linkColor: Theme.accent
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
// Markdown's own line height is tight for a wall of prose at this
// size; a document read once needs to be easy on the first pass.
lineHeight: 1.35
// A link that names a settings page opens that page instead of a
// browser, which is the whole reason the manual lives inside
// Settings. Anything else -- a project URL, a wiki -- still leaves
// the desktop.
//
// The scheme is Panama's own, so a chapter can never accidentally
// hand an http link to openSettings, and an unknown page cannot be
// smuggled in either: SettingsRoutes.resolve turns anything it does
// not recognize into Home rather than a blank loader. A fragment,
// where there is one, is the section within the page.
onLinkActivated: link => {
const target = String(link ?? "");
const scheme = "panama://settings/";
if (target.indexOf(scheme) !== 0) {
Qt.openUrlExternally(target);
return;
}
const rest = target.slice(scheme.length);
const hash = rest.indexOf("#");
if (hash < 0)
ShellState.openSettings(rest);
else
ShellState.openSettingsSection(rest.slice(0, hash), rest.slice(hash + 1));
}
}
}
}