docs(nav): Administration section lives in docs/admin/; sidebar ordered by usability
Two structural changes, one owner-approved plan: - Move the admin guides out of docs/user/ into their own docs/admin/ area — admin-guide.md becomes the Administration section landing page (pinned first), and the 32-line admin-library-management.md stub becomes admin/libraries.md, which the admin-docs build-out rebuilds into the real Libraries page next. Retired URLs 301 via a new docRedirects map in the docs HTTP handler (checked before .md canonicalization so even .md spellings one-hop to the new clean URL); the two inbound links (docs index, user-guide front door) updated. - Sidebar direct items now sort by usabilityOrder — an explicit, per-section display order — instead of alphabetically: User Guide mirrors the 'Where to go' table on user-guide.md, Devices orders Android → KOReader → Kobo (alphabetically Kobo used to sort ahead of KOReader), and Administration pre-lists its planned pages so each lands in place as the admin build writes it. Unlisted docs fall back to alphabetical after the listed ones. Display-order only by construction: the URL-driven expand/highlight machinery keys on containsURL membership and never sees sort order — nav tests updated to pin the new orders plus a real-tree test (TestBuildNavigationUsabilityOrder) guarding all three sections.
This commit is contained in:
1 parent
7e2e1c4c60
commit
393a00e8dd
7 files changed
+152
-15
No files matched your search
@@ -28,6 +28,47 @@ var sectionOrder = []struct {
|
||||
{Title: "Contributing", Icon: "heart"},
|
||||
}
|
||||
|
||||
// usabilityOrder pins an explicit, usability-first display order for a
|
||||
// section's direct nav items, overriding the alphabetical fallback (owner
|
||||
// request 2026-11-02: order the menu by how people work, not by letter —
|
||||
// the User Guide list mirrors the "Where to go" table on user-guide.md).
|
||||
// Values are doc paths without the .md suffix; anything in the section not
|
||||
// listed sorts alphabetically AFTER the listed items. Display-only: the
|
||||
// open/highlight machinery keys on URL membership (containsURL) and is
|
||||
// order-blind, so URL-driven expansion is unaffected. Administration lists
|
||||
// its planned pages up front so each lands in place as it is written.
|
||||
var usabilityOrder = map[string][]string{
|
||||
"User Guide": {
|
||||
"user/dashboard",
|
||||
"user/library-browsing",
|
||||
"user/collections",
|
||||
"user/searching-and-filtering",
|
||||
"user/podcasts",
|
||||
"user/reader-guide",
|
||||
"user/themes",
|
||||
"user/profile-guide",
|
||||
"user/sync-guide",
|
||||
"user/calibre-integration",
|
||||
},
|
||||
"Devices": {
|
||||
"user/devices/android-app",
|
||||
"user/devices/koreader-setup",
|
||||
"user/devices/kobo-setup",
|
||||
},
|
||||
"Administration": {
|
||||
"admin/admin-guide",
|
||||
"admin/dashboard",
|
||||
"admin/libraries",
|
||||
"admin/library-processing-issues",
|
||||
"admin/archived-items",
|
||||
"admin/podcasts",
|
||||
"admin/hash-conflicts",
|
||||
"admin/users",
|
||||
"admin/settings",
|
||||
"admin/scanning",
|
||||
},
|
||||
}
|
||||
|
||||
// groupTitleOverrides fixes casing that naive title-casing gets wrong.
|
||||
var groupTitleOverrides = map[string]string{
|
||||
"admin": "Admin",
|
||||
@@ -104,12 +145,16 @@ func placeDoc(docPath string) navPlacement {
|
||||
if rest == "user-guide.md" {
|
||||
return navPlacement{section: "User Guide", pin: -50}
|
||||
}
|
||||
// Admin-facing guides are their own top-level section too.
|
||||
if strings.HasPrefix(rest, "admin-") {
|
||||
return navPlacement{section: "Administration"}
|
||||
}
|
||||
return navPlacement{section: "User Guide"}
|
||||
|
||||
case strings.HasPrefix(docPath, "admin/"):
|
||||
// Admin docs live in their own area (docs/admin/); the guide is
|
||||
// the section landing page.
|
||||
if docPath == "admin/admin-guide.md" {
|
||||
return navPlacement{section: "Administration", pin: -50}
|
||||
}
|
||||
return navPlacement{section: "Administration"}
|
||||
|
||||
case strings.HasPrefix(docPath, "developer/api/"):
|
||||
rest := strings.TrimPrefix(docPath, "developer/api/")
|
||||
if dir, _, ok := strings.Cut(rest, "/"); ok {
|
||||
@@ -256,14 +301,27 @@ func (h *DocsHandler) BuildNavigation(currentPath string) *templates.Navigation
|
||||
build := builds[spec.Title]
|
||||
section := templates.NavSection{Title: spec.Title, Icon: spec.Icon}
|
||||
|
||||
// Direct items: pinned landing pages lead, then alphabetical
|
||||
// (case-insensitive — KOReader must not sort before Kobo).
|
||||
// Direct items: pinned landing pages lead, then usability order
|
||||
// (see usabilityOrder), then alphabetical among anything unlisted.
|
||||
order := usabilityOrder[spec.Title]
|
||||
rankOf := func(url string) int {
|
||||
for i, p := range order {
|
||||
if docURL(p) == url {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return len(order)
|
||||
}
|
||||
items := append([]templates.NavItem(nil), build.items...)
|
||||
sort.SliceStable(items, func(i, j int) bool {
|
||||
pi, pj := build.pinned[items[i].URL], build.pinned[items[j].URL]
|
||||
if pi != pj {
|
||||
return pi
|
||||
}
|
||||
ri, rj := rankOf(items[i].URL), rankOf(items[j].URL)
|
||||
if ri != rj {
|
||||
return ri < rj
|
||||
}
|
||||
return strings.ToLower(items[i].Title) < strings.ToLower(items[j].Title)
|
||||
})
|
||||
section.Items = items
|
||||
|
||||
Reference in new issue
Block a user