113 lines
4.2 KiB
QML
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 recognise 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));
|
|
}
|
|
}
|
|
}
|
|
}
|