Write the manual for the person using the desktop
docs/ is engineering artifacts -- design specs, plans, an upstream ledger -- and none of it is written for the person who has to live here. Five chapters that are: coming from another desktop, the keyboard, windows and workspaces, when something breaks, and making it yours. Rendered inside Settings rather than opened in a browser, so a chapter that says "the Displays page reverts after fifteen seconds" is one click from the Displays page. Qt's markdown renderer does the work; one chapter per Text, because Text has an implicit texture size limit and a document long enough to hit it goes blank rather than complaining. The chapters live beside the shell in manual/ rather than at the repository root, which departs from the plan. The reason is the path: the shell finds them through Quickshell.shellDir, which is correct wherever the repository is, whereas walking upward out of the shell directory is only correct by accident. The contract fails that pattern if it comes back. The contract also pins the set both ways -- every chapter listed exists, every chapter that exists is listed -- because a renamed file shows an error card where a chapter should be, which reads as a broken manual rather than as a moved file.
This commit is contained in:
@@ -108,12 +108,13 @@ setup/
|
|||||||
packages/ One package per line; extras/ holds the optional categories
|
packages/ One package per line; extras/ holds the optional categories
|
||||||
scripts/ Run in order by ./install
|
scripts/ Run in order by ./install
|
||||||
tests/ Contracts. See below
|
tests/ Contracts. See below
|
||||||
|
manual/ The user manual, rendered in Settings
|
||||||
docs/ Settings reference, and the design specs behind the work
|
docs/ Settings reference, and the design specs behind the work
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
142 of them, under `tests/`. Run the lot, or a subset by pattern:
|
143 of them, under `tests/`. Run the lot, or a subset by pattern:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
panama test # everything
|
panama test # everything
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# Coming from another desktop
|
||||||
|
|
||||||
|
Panama is Hyprland with a shell built to replace a GNOME session, so most of
|
||||||
|
what you know still applies. The differences worth knowing on day one are
|
||||||
|
below. Nothing here is a preference you have to accept: every one of them is
|
||||||
|
adjustable in Settings.
|
||||||
|
|
||||||
|
## Windows arrange themselves
|
||||||
|
|
||||||
|
The biggest change. You do not drag windows into position. Open a second
|
||||||
|
window and the screen splits; open a third and it splits again. This is
|
||||||
|
called tiling, and after a day of it, dragging windows feels like making
|
||||||
|
your own bed in a hotel.
|
||||||
|
|
||||||
|
Two things follow from it:
|
||||||
|
|
||||||
|
- **There is no minimise.** A minimised window is a window you have hidden
|
||||||
|
from a layout that was managing it for you. Use `Super + Shift + X` to
|
||||||
|
push a window to the scratchpad and `Super + X` to bring it back, which is
|
||||||
|
the same idea without the layout losing track of it.
|
||||||
|
- **Floating still exists.** `Super + Ctrl + C` makes a window float above
|
||||||
|
the layout, for the dialog that was never meant to be tiled.
|
||||||
|
|
||||||
|
## Workspaces replace the taskbar
|
||||||
|
|
||||||
|
Instead of many windows on one screen, use many screens. `Alt + 1` through
|
||||||
|
`Alt + 0` jump straight to a workspace; `Alt + Shift + 1` sends the window
|
||||||
|
you are looking at to workspace one. `Super + backtick` shows all of them at
|
||||||
|
once.
|
||||||
|
|
||||||
|
If you came from macOS, this is Spaces with the friction removed. If you came
|
||||||
|
from Windows, it is virtual desktops that are actually worth using because
|
||||||
|
switching is one key rather than three.
|
||||||
|
|
||||||
|
## The launcher is the menu
|
||||||
|
|
||||||
|
There is no applications menu to browse. `Super + Space` opens a search box
|
||||||
|
that finds applications, files, clipboard history, emoji, and does
|
||||||
|
arithmetic. Type three letters of what you want.
|
||||||
|
|
||||||
|
This replaces the Start menu, Spotlight, the GNOME overview's app grid, and
|
||||||
|
the run dialog, and it is faster than all of them because it never asks you
|
||||||
|
to aim at anything.
|
||||||
|
|
||||||
|
## What stayed the same
|
||||||
|
|
||||||
|
`Super + Tab` still switches windows. The bar is still along the top with a
|
||||||
|
clock in the middle and status on the right. There is still a dock. Volume
|
||||||
|
and brightness keys still work, and still work on the lock screen. Copy and
|
||||||
|
paste are unchanged. Your files are where you left them.
|
||||||
|
|
||||||
|
GNOME Settings is still installed for the few panels Panama deliberately does
|
||||||
|
not own, and Panama's own Settings hands off to it where that applies.
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# The keyboard
|
||||||
|
|
||||||
|
Press `Super + /` at any time to see every shortcut this machine has. That
|
||||||
|
list is generated from the live keymap, so it is never out of date, and it
|
||||||
|
includes anything you have rebound.
|
||||||
|
|
||||||
|
This chapter is the shape behind that list, which is the part worth learning
|
||||||
|
once.
|
||||||
|
|
||||||
|
## Three modifiers, three jobs
|
||||||
|
|
||||||
|
| Modifier | Acts on |
|
||||||
|
|---|---|
|
||||||
|
| `Super` | The window you are looking at |
|
||||||
|
| `Alt` | Workspaces |
|
||||||
|
| `Super + Ctrl` | The layout itself: splitting, floating, swapping |
|
||||||
|
|
||||||
|
Almost every shortcut follows from this. `Super + H` moves focus left.
|
||||||
|
`Alt + H` moves to the workspace on the left. `Super + Ctrl + H` swaps the
|
||||||
|
window leftward in the layout.
|
||||||
|
|
||||||
|
## Directions
|
||||||
|
|
||||||
|
`H`, `J`, `K`, `L` mean left, down, up, right, as they do in vim. The arrow
|
||||||
|
keys work everywhere the letters do, so you never have to learn them if you
|
||||||
|
would rather not.
|
||||||
|
|
||||||
|
Add `Shift` to move the window rather than the focus. `Super + Shift + L`
|
||||||
|
takes the current window and moves it to the right.
|
||||||
|
|
||||||
|
## The handful worth memorising first
|
||||||
|
|
||||||
|
| Keys | What it does |
|
||||||
|
|---|---|
|
||||||
|
| `Super + Space` | Find and open anything |
|
||||||
|
| `Super + T` | Terminal |
|
||||||
|
| `Super + Q` | Close the window |
|
||||||
|
| `Super + backtick` | Every window, every workspace |
|
||||||
|
| `Super + I` | Settings |
|
||||||
|
| `Super + /` | Every other shortcut |
|
||||||
|
|
||||||
|
## Changing them
|
||||||
|
|
||||||
|
Settings has a Shortcuts page listing every bind, each of which can be
|
||||||
|
reassigned. A rebind moves the shortcut and cannot change what it does, so
|
||||||
|
there is no way to make a key do something unexpected by editing it.
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# Windows and workspaces
|
||||||
|
|
||||||
|
## How the split is chosen
|
||||||
|
|
||||||
|
A new window splits the space of the window that had focus. Whether it splits
|
||||||
|
beside or below depends on which way that space is longer, which usually
|
||||||
|
matches what you were about to do anyway.
|
||||||
|
|
||||||
|
When it does not, `Super + Ctrl + G` flips the split of the focused window,
|
||||||
|
and `Super + Ctrl + Z` and `Super + Ctrl + V` decide the direction the *next*
|
||||||
|
window will take before you open it.
|
||||||
|
|
||||||
|
## Resizing
|
||||||
|
|
||||||
|
`Super + Shift` plus `Y`, `O`, `B`, `M` adjusts width; `I`, `U`, `N` and
|
||||||
|
comma adjust height. These are inverted from what their letters suggest, on
|
||||||
|
purpose, and the reason is written in `config/dot/hypr/keybinds.lua` next to
|
||||||
|
the binds.
|
||||||
|
|
||||||
|
For anything finer, hold `Super` and drag with the right mouse button.
|
||||||
|
|
||||||
|
## Fullscreen and floating
|
||||||
|
|
||||||
|
`Super + U` makes the focused window fullscreen. `Super + Ctrl + C` lifts it
|
||||||
|
out of the layout to float, and `Super + Ctrl + Shift + C` pins it so it
|
||||||
|
follows you between workspaces. A floating window can be moved by holding
|
||||||
|
`Super` and dragging with the left button.
|
||||||
|
|
||||||
|
## Workspaces
|
||||||
|
|
||||||
|
Ten of them, on `Alt + 1` through `Alt + 0`. `Alt + H` and `Alt + L` step
|
||||||
|
between neighbours, and `Alt + Shift` plus a number sends the current window
|
||||||
|
somewhere without following it.
|
||||||
|
|
||||||
|
On a machine with more than one display, workspaces belong to whichever
|
||||||
|
screen has focus. If you would rather pin workspaces one through ten to your
|
||||||
|
main display and give the second screen its own, that is a switch on the
|
||||||
|
Desktop settings page.
|
||||||
|
|
||||||
|
## Projects
|
||||||
|
|
||||||
|
A project is the set of windows you open together: which applications, which
|
||||||
|
workspace each was on, and for a terminal, which directory it was in.
|
||||||
|
|
||||||
|
Arrange things the way you want them, then run **Save Layout as Project**
|
||||||
|
from the launcher. **Open Project** lays it out again on free workspaces, so
|
||||||
|
it never lands on top of what you are already doing. An application that
|
||||||
|
refuses to open twice is moved into place rather than launched again.
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# When something breaks
|
||||||
|
|
||||||
|
## Start here
|
||||||
|
|
||||||
|
Run **Check System Health** from the launcher, or open Settings and go to
|
||||||
|
System Health. It reports what is actually running rather than what was
|
||||||
|
installed, and it can repair several things itself.
|
||||||
|
|
||||||
|
From a terminal, the same check is `panama doctor`.
|
||||||
|
|
||||||
|
## The shell is gone
|
||||||
|
|
||||||
|
If the bar and dock disappear, the Quickshell process died. **Restart Shell**
|
||||||
|
from the launcher brings it back. If the launcher is also gone, press
|
||||||
|
`Super + Shift + R` for the fallback launcher, which depends on nothing but
|
||||||
|
itself and exists for exactly this.
|
||||||
|
|
||||||
|
From a terminal: `qs -d` starts the shell again.
|
||||||
|
|
||||||
|
## A shortcut stopped working
|
||||||
|
|
||||||
|
Check `Super + /` first: if the shortcut shows a different chord than you
|
||||||
|
expect, something rebound it. The Shortcuts settings page can reset any bind,
|
||||||
|
or all of them at once.
|
||||||
|
|
||||||
|
If the whole keymap is missing from that list, the compositor config failed
|
||||||
|
to load. `Hyprland --verify-config` says why without touching your session.
|
||||||
|
|
||||||
|
## The screen resolution is wrong
|
||||||
|
|
||||||
|
Settings has a Displays page. Every change there reverts itself after fifteen
|
||||||
|
seconds unless you confirm it, so a mode your monitor cannot show cannot
|
||||||
|
strand you. If you are already stranded, `hyprctl monitors` from a terminal
|
||||||
|
shows what is applied.
|
||||||
|
|
||||||
|
## Something asked for a password and I do not know why
|
||||||
|
|
||||||
|
Panama's password prompt names the action that asked. If it was started by an
|
||||||
|
agent or a script through `panama-sudo`, it also shows a stated reason,
|
||||||
|
labelled as an unverified claim, beside the real action.
|
||||||
|
|
||||||
|
If you did not expect it at all, cancel. Nothing that needs privileges is
|
||||||
|
harmed by being refused.
|
||||||
|
|
||||||
|
## After an update, something is not right
|
||||||
|
|
||||||
|
`panama migrate` applies repairs this machine has not had yet. It is safe to
|
||||||
|
run at any time and does nothing when there is nothing to do.
|
||||||
|
|
||||||
|
If an update went badly, the Snapshots settings page can roll the system back
|
||||||
|
where btrfs snapshots are configured.
|
||||||
|
|
||||||
|
## Reporting it
|
||||||
|
|
||||||
|
`panama doctor` produces a redacted summary suitable for pasting into an
|
||||||
|
issue. It deliberately omits tokens, hostnames and account details.
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Making it yours
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
`Super + I`. Thirty-odd pages covering displays, sound, network, appearance,
|
||||||
|
power, printers, users, sharing, storage, updates and the rest. The search
|
||||||
|
box at the top searches the settings themselves rather than just page names,
|
||||||
|
so if you know what you want to change you can type it.
|
||||||
|
|
||||||
|
## Appearance
|
||||||
|
|
||||||
|
Panama ships Tokyo Night Moon in a light and a dark variant, with eight
|
||||||
|
accent colours. Changing either repaints the shell, the terminal, GTK
|
||||||
|
applications, the lock screen and the system monitor at once.
|
||||||
|
|
||||||
|
The wallpaper is per-monitor if you want it to be.
|
||||||
|
|
||||||
|
## Applications
|
||||||
|
|
||||||
|
`panama apps` in a terminal offers the optional application categories the
|
||||||
|
installer asked about, so a machine can acquire Slack in March without having
|
||||||
|
wanted Discord in January.
|
||||||
|
|
||||||
|
`panama app` builds the handful of applications no repository carries. It is
|
||||||
|
deliberately not part of installing, because a source build is slow and
|
||||||
|
depends on an upstream that moves.
|
||||||
|
|
||||||
|
## Autostart and defaults
|
||||||
|
|
||||||
|
The Applications settings page controls which application opens which kind of
|
||||||
|
file, and what starts when you log in.
|
||||||
|
|
||||||
|
## Going further
|
||||||
|
|
||||||
|
The compositor is configured in Lua under `~/.config/hypr`, and every file
|
||||||
|
there explains what it does and why. `overrides.lua` is loaded last and is
|
||||||
|
the intended place for per-machine changes, so you can adjust anything
|
||||||
|
without editing a file the repository will update.
|
||||||
|
|
||||||
|
The shell is QML under `~/.config/quickshell`. Both directories are symlinks
|
||||||
|
into the Panama repository, so an edit is a change to your checkout and
|
||||||
|
`panama update` will offer to commit it.
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// The chapters live beside the shell in manual/, not at the repository root.
|
||||||
|
// They are read at runtime through Quickshell.shellDir, which resolves whether
|
||||||
|
// or not the repository is where it usually is; a path walked upward out of
|
||||||
|
// the shell directory does not.
|
||||||
|
//
|
||||||
|
// 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."
|
||||||
|
|
||||||
|
// Chapter files are numbered so their order is their filename. The title
|
||||||
|
// shown here is the first heading of each, read from the file, so a
|
||||||
|
// chapter cannot be renamed in one place and not the other.
|
||||||
|
readonly property var chapters: [
|
||||||
|
{ file: "01-coming-from-another-desktop.md", label: "Coming from another desktop" },
|
||||||
|
{ file: "02-the-keyboard.md", label: "The keyboard" },
|
||||||
|
{ file: "03-windows-and-workspaces.md", label: "Windows and workspaces" },
|
||||||
|
{ file: "04-when-something-breaks.md", label: "When something breaks" },
|
||||||
|
{ file: "05-making-it-yours.md", label: "Making it yours" }
|
||||||
|
]
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsTabs {
|
||||||
|
tabs: root.chapters.map((chapter, index) => ({
|
||||||
|
value: String(index),
|
||||||
|
label: chapter.label
|
||||||
|
}))
|
||||||
|
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
|
||||||
|
|
||||||
|
onLinkActivated: link => Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -131,6 +131,7 @@ Rectangle {
|
|||||||
case "containers": return containersPage;
|
case "containers": return containersPage;
|
||||||
case "ssh-keys": return sshKeysPage;
|
case "ssh-keys": return sshKeysPage;
|
||||||
case "services": return healthPage;
|
case "services": return healthPage;
|
||||||
|
case "manual": return manualPage;
|
||||||
case "about": return aboutPage;
|
case "about": return aboutPage;
|
||||||
default: return homePage;
|
default: return homePage;
|
||||||
}
|
}
|
||||||
@@ -195,6 +196,7 @@ Rectangle {
|
|||||||
Component { id: regionPage; RegionPage {} }
|
Component { id: regionPage; RegionPage {} }
|
||||||
Component { id: onlineAccountsPage; OnlineAccountsPage {} }
|
Component { id: onlineAccountsPage; OnlineAccountsPage {} }
|
||||||
Component { id: healthPage; HealthPage {} }
|
Component { id: healthPage; HealthPage {} }
|
||||||
|
Component { id: manualPage; ManualPage {} }
|
||||||
Component { id: aboutPage; AboutPage {} }
|
Component { id: aboutPage; AboutPage {} }
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ Rectangle {
|
|||||||
{ page: "snapshots", label: "Snapshots", icon: "\u{F0954}" },
|
{ page: "snapshots", label: "Snapshots", icon: "\u{F0954}" },
|
||||||
{ page: "users", label: "Users", icon: "\u{F0004}" },
|
{ page: "users", label: "Users", icon: "\u{F0004}" },
|
||||||
{ page: "services", label: "System Health", icon: "\u{F0493}" },
|
{ page: "services", label: "System Health", icon: "\u{F0493}" },
|
||||||
|
{ page: "manual", label: "Manual", icon: "\u{F02D4}" },
|
||||||
{ page: "about", label: "About", icon: "\u{F02FD}" }
|
{ page: "about", label: "About", icon: "\u{F02FD}" }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
module qs.modules.settings
|
module qs.modules.settings
|
||||||
AboutPage 1.0 AboutPage.qml
|
AboutPage 1.0 AboutPage.qml
|
||||||
|
ManualPage 1.0 ManualPage.qml
|
||||||
AppearancePage 1.0 AppearancePage.qml
|
AppearancePage 1.0 AppearancePage.qml
|
||||||
AvatarPicker 1.0 AvatarPicker.qml
|
AvatarPicker 1.0 AvatarPicker.qml
|
||||||
ConnectivityPage 1.0 ConnectivityPage.qml
|
ConnectivityPage 1.0 ConnectivityPage.qml
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ Singleton {
|
|||||||
// them rather than Panama. Without these, searching "timezone" would fail
|
// them rather than Panama. Without these, searching "timezone" would fail
|
||||||
// on a settings app that plainly has one.
|
// on a settings app that plainly has one.
|
||||||
readonly property var extraEntries: [
|
readonly property var extraEntries: [
|
||||||
|
{ label: "Manual", detail: "How this desktop works, in chapters", page: "manual" },
|
||||||
|
{ label: "Getting started", detail: "Coming from GNOME, macOS or Windows", page: "manual" },
|
||||||
|
{ label: "Troubleshooting", detail: "When something breaks", page: "manual" },
|
||||||
|
{ label: "Help", detail: "The manual", page: "manual" },
|
||||||
{ label: "Timezone", detail: "Set the system timezone", page: "datetime" },
|
{ label: "Timezone", detail: "Set the system timezone", page: "datetime" },
|
||||||
{ label: "Network time", detail: "Synchronize the clock with a time server", page: "datetime" },
|
{ label: "Network time", detail: "Synchronize the clock with a time server", page: "datetime" },
|
||||||
{ label: "Wi-Fi", detail: "Managed by GNOME Settings", page: "connectivity" },
|
{ label: "Wi-Fi", detail: "Managed by GNOME Settings", page: "connectivity" },
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showSettings(page: string): void {
|
function showSettings(page: string): void {
|
||||||
const allowed = ["home", "appearance", "displays", "connectivity", "home-phone", "desktop", "sound", "gaming", "notifications", "screen-intelligence", "shortcuts", "mouse", "privacy", "region", "accounts", "accessibility", "power", "datetime", "applications", "updates", "storage", "snapshots", "users", "sharing", "firewall", "printers", "containers", "ssh-keys", "services", "about"];
|
const allowed = ["home", "appearance", "displays", "connectivity", "home-phone", "desktop", "sound", "gaming", "notifications", "screen-intelligence", "shortcuts", "mouse", "privacy", "region", "accounts", "accessibility", "power", "datetime", "applications", "updates", "storage", "snapshots", "users", "sharing", "firewall", "printers", "containers", "ssh-keys", "services", "manual", "about"];
|
||||||
root.settingsPage = allowed.indexOf(page) >= 0 ? page : "home";
|
root.settingsPage = allowed.indexOf(page) >= 0 ? page : "home";
|
||||||
DesktopPreferences.set("lastPage", root.settingsPage);
|
DesktopPreferences.set("lastPage", root.settingsPage);
|
||||||
root.settingsOpen = true;
|
root.settingsOpen = true;
|
||||||
|
|||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Generated by scripts/panama-settings-commands -- do not edit by hand.
|
||||||
|
# @vicinae.schemaVersion 1
|
||||||
|
# @vicinae.title Settings: Manual
|
||||||
|
# @vicinae.mode silent
|
||||||
|
# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg
|
||||||
|
# @vicinae.description Open Manual in Settings.
|
||||||
|
# @vicinae.keywords ["settings", "manual", "getting started", "troubleshooting", "help"]
|
||||||
|
|
||||||
|
exec "$HOME/.config/quickshell/scripts/panama-action" settings-page manual
|
||||||
Executable
+97
@@ -0,0 +1,97 @@
|
|||||||
|
#!/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[@]}"
|
||||||
Reference in New Issue
Block a user