help

package
v0.0.21 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package help is the runtime's inline-help index. Apps that ship Markdown documentation populate app.Manifest.Help with an fs.FS (typically an embed.FS rooted at the app's `help/` directory); the DefaultLibrary lazily builds a BookI per app on first access.

The package is intentionally render-agnostic: it owns parsing and indexing (frontmatter, headings, doc paths) and hands the parsed markdown.Doc back to the consumer. A help-reader app, a tooltip popup, or a CLI exporter all consume the same BookI surface.

The library auto-syncs against app.DefaultRegistry on first use, so apps that already register against the runtime get help indexing for free as soon as Manifest.Help is populated. Explicit Register / SyncFromRegistry hooks exist for tests and special wiring.

What this package does NOT do (deferred to follow-up rounds):

  • Open help over the bus. RefT is the typed payload a future `runtime.help.open` cap subject will carry; the bus subscription and a HelpHost app land separately.
  • Resolve cross-app wikilinks. The markdown widget's resolver.ResolverI hook is where `[[appid/doc#section]]` will be lowered to RefT, also in a follow-up round.

Full-text search over the corpus lives in the search sub-package (pattern batteries over section-grained source slices, ADR-0164); this package only records the byte offsets that make the slicing possible (SectionInfo.ByteOffset).

Index

Constants

This section is empty.

Variables

View Source
var PackageProps = packageprops.Props{
	WASMWASI:         packageprops.WASMBlocked,
	WASMJS:           packageprops.WASMCompiles,
	WASMFreestanding: packageprops.WASMCompiles,
}

PackageProps records this package's curated properties (ADR-0080). Seeded by `boxer code analysis golang wasmsurvey props generate`; curate by hand. The same group's `props verify` reconciles it.

Functions

func MustSub

func MustSub(fsys fs.FS, dir string) (sub fs.FS)

MustSub returns the fs.Sub view of fsys rooted at dir. Panics when the lookup fails, which surfaces a misaligned `//go:embed` directive at init() time instead of letting BookI silently index an empty corpus. The canonical use site is one line in the app's manifest declaration:

//go:embed help
var helpFS embed.FS

var manifest = app.Manifest{
    ...
    Help: help.MustSub(helpFS, "help"),
}

The named subdirectory is what the embed directive listed; with the `//go:embed help` form above, that's `"help"`. Apps that ship docs from a differently-named directory (e.g., `//go:embed manual`) pass that name instead.

func Register

func Register(b BookI) (err error)

Register is a package-level shortcut for [DefaultLibrary.Register].

func SyncFromRegistry

func SyncFromRegistry() (added int)

SyncFromRegistry is a package-level shortcut for [DefaultLibrary.SyncFromRegistry].

Types

type BookI

type BookI interface {
	// AppId returns the app this book belongs to. Stable across the
	// book's lifetime.
	AppId() (id app.AppIdT)
	// Docs returns the indexed documents in path-sorted order. Triggers
	// a one-shot walk + parse on first call.
	Docs() (docs []DocInfo)
	// Doc returns the parsed markdown document and its DocInfo for the
	// given FS-relative path minus the `.md` suffix (e.g. `"overview"`
	// or `"howto/replay"`). ok=false when the path is not indexed.
	Doc(docPath string) (doc *markdown.Doc, info DocInfo, ok bool)
	// Source returns the raw markdown bytes the doc was parsed from.
	// Consumers that want to display a "view source" toggle (such as
	// the HelpHost app) read from here and feed the bytes through
	// codeview.PrepareMarkdown for syntax-highlighted rendering. The
	// returned slice is owned by the book and MUST NOT be mutated.
	// ok=false when the path is not indexed.
	Source(docPath string) (src []byte, ok bool)
	// HasSection reports whether the document at docPath contains a
	// heading whose slug matches section. ok=false when the doc is
	// absent or the section is unknown. Used by [RefT] consumers to
	// validate cross-document links at write time.
	HasSection(docPath string, section string) (ok bool)
	// Validate reports documentation-standard front-matter conformance
	// problems across every indexed document, in path-sorted order (empty
	// when the book conforms). Operator-facing, so `type: adr` is rejected;
	// see [ValidateDocInfo] for the per-document check. Triggers the
	// one-shot walk on first call, like the other index methods.
	Validate() (problems []Problem)
}

BookI is the parsed help corpus for one app. Implementations are lazy: the first call to BookI.Docs, BookI.Doc, or BookI.HasSection walks the fs.FS, parses every .md file, and caches the result for the book's lifetime. Subsequent calls hit the cache.

Failures during the walk (missing FS entries, parse errors) are logged at Warn but do not poison the cache — the affected file is simply absent from BookI.Docs. Callers that need hard-fail semantics should use the NewBook error return on construction (a nil fs.FS) and treat per-file gaps as drift to be fixed in CI.

func Book

func Book(id app.AppIdT) (b BookI, ok bool)

Book is a package-level shortcut for [DefaultLibrary.Book].

func Books

func Books() (books []BookI)

Books is a package-level shortcut for [DefaultLibrary.Books].

func NewBook

func NewBook(id app.AppIdT, fsys fs.FS) (b BookI, err error)

NewBook constructs a BookI over fsys. The book holds the fs.FS reference but does no I/O until one of the index methods is called; callers can therefore construct books cheaply at init() and amortise the parse cost across actual help opens.

Returns an error only when fsys is nil — every other failure mode (missing files, parse errors, no .md content) degrades to an empty index that the consumer can render as "no help available for this app".

type DocInfo

type DocInfo struct {
	Path     string
	Title    string
	Type     string
	Status   string
	Sections []SectionInfo
}

DocInfo is the static metadata of one help document. Populated at first-parse time and cached on the BookI for the book's lifetime.

Title resolution order: frontmatter `title:` → first H1 → filename leaf. Type and Status come from the Diátaxis-mandated frontmatter keys ([CLAUDE.md]); empty strings when the doc omits them. Sections is the in-document heading list in document order, suitable as a TOC sidebar source.

type FSImageResolver

type FSImageResolver struct {
	resolver.NoopResolver
	// contains filtered or unexported fields
}

FSImageResolver decodes inline image references in help docs against a backing fs.FS. URL resolution for wikilinks and embeds is delegated to resolver.NoopResolver; only image bytes are served from the FS.

Image refs in markdown — both CommonMark `![alt](path)` and Obsidian `![[file.png]]` — arrive as path strings rooted at the FS root. A leading `/` is stripped so `![](/logo.png)` and `![](logo.png)` resolve to the same FS entry; otherwise paths are interpreted verbatim against fs.ReadFile, which handles nested directories (`assets/diag.png`) naturally.

Relative paths (`../assets/`) are intentionally not supported in M1 — every doc inside one BookI shares the same FS root, so vault-rooted refs are the unambiguous form. Future work can layer per-doc base-path resolution on top if authors want relative refs.

func NewFSImageResolver

func NewFSImageResolver(fsys fs.FS) (r FSImageResolver)

NewFSImageResolver constructs a resolver that loads images from fsys and inherits NoopResolver's URL behaviour for wikilinks / embeds. A nil fsys is valid — [LoadImage] returns ok=false for any ref, which matches NoopResolver's "no images" baseline.

func (FSImageResolver) LoadImage

func (inst FSImageResolver) LoadImage(ref string) (pixels []uint32, widthPx uint32, heightPx uint32, ok bool)

LoadImage reads ref from the backing fs.FS and decodes it into RGBA8 pixels for the markdown widget's inline image run. ok=false (and a nil pixel slice) is returned when the file is missing, the FS is nil, or the bytes don't decode as one of the formats imagedecode.DecodeRGBA8 registers — the markdown widget then falls back to the glyph-prefixed hyperlink rendering.

The decode is bounded by imagedecode.DefaultMaxPixels even though help assets ship inside the binary and are not attacker-supplied: a bound costs one header read, and FSImageResolver is constructed from whatever fs.FS a caller hands it, which need not be an embed.FS.

type LibraryI

type LibraryI interface {
	// Book returns the book registered for id, or ok=false when absent.
	// Triggers a one-shot SyncFromRegistry on first call.
	Book(id app.AppIdT) (b BookI, ok bool)
	// Books returns every registered book in AppId-sorted order.
	// Triggers a one-shot SyncFromRegistry on first call.
	Books() (books []BookI)
	// Register inserts an explicitly-built book. Returns an error on
	// nil book or on duplicate AppId (first registration wins).
	Register(b BookI) (err error)
	// SyncFromRegistry walks app.DefaultRegistry and registers a fresh
	// Book for every Manifest with a non-nil Help fs.FS that isn't
	// already registered. Returns the number of books added. Safe to
	// call repeatedly; idempotent past the first call.
	SyncFromRegistry() (added int)
}

LibraryI is the registry of BookI values keyed by app.AppIdT. Implementations auto-sync from app.DefaultRegistry on first read — every registered app.Manifest with a non-nil Help fs.FS becomes a book on first access — so the typical app does not need to call LibraryI.Register explicitly.

Manual registration is supported for tests, special-purpose libraries built around a [Registry] other than DefaultRegistry, and runtime-injected docs (e.g. bundled "About Keelson" content that isn't owned by any single app).

var DefaultLibrary LibraryI = NewLibrary()

DefaultLibrary is the process-wide library populated by the auto-sync path. Mirrors app.DefaultRegistry's role.

func NewLibrary

func NewLibrary() (l LibraryI)

NewLibrary returns an empty library suitable for tests or special wiring. Production code uses DefaultLibrary.

type Problem

type Problem struct {
	// DocPath is the FS-relative path (minus the `.md` suffix) of the
	// offending document — the same key [BookI.Doc] takes.
	DocPath string
	// Field is the offending front-matter key: "type" or "status".
	Field string
	// Value is the offending value, or "" when the field is absent.
	Value string
	// Message is a self-contained, human-readable description of the breach.
	Message string
}

Problem is one documentation-standard front-matter conformance issue for a single help document, as surfaced by ValidateDocInfo and BookI.Validate. It carries the doc path so a caller iterating a whole book can attribute the breach; field, value, and message come straight from the shared docstd.Violation.

func ValidateDocInfo

func ValidateDocInfo(info DocInfo) (problems []Problem)

ValidateDocInfo checks one document's parsed front-matter (its `type` and `status`) against the boxer documentation standard and returns one Problem per breach. An empty result means the doc conforms.

Inline help is operator-facing, so `type: adr` is rejected here even though repo-wide linting accepts it — an ADR is design history, not help (see docstd.ValidateFrontmatter). Title is deliberately not validated: the library's frontmatter→H1→filename fallback (see DocInfo) makes a missing `title:` a non-issue by design.

type RefT

type RefT struct {
	AppId   app.AppIdT
	Doc     string
	Section string
}

RefT references one help location: a book (selected by app.AppIdT), a document inside that book (FS-relative path minus the .md suffix, e.g. `"overview"` or `"howto/replay"`), and an optional heading slug inside that document (matches markdown.SlugHeading).

RefT is a plain value: it will round-trip through CBOR on the bus (once the bus subject lands) and across Go function boundaries today. The String form is for debug and log output, not a wire format.

func (RefT) IsZero

func (inst RefT) IsZero() (zero bool)

IsZero reports whether the ref is the zero value. Useful for "no target" guards in tooltip-style consumers.

func (RefT) String

func (inst RefT) String() (s string)

String returns a human-readable rendering of the ref for logs and error messages: `"<AppId>/<Doc>[#<Section>]"`. Not a parseable canonical form — RefT travels as a struct, not a URL, until the bus subject lands.

type SectionInfo

type SectionInfo struct {
	Slug       string
	Text       string
	Level      uint8
	ByteOffset int
}

SectionInfo describes one top-level heading inside a help document. Slug matches markdown.SlugHeading so RefT.Section values resolve to the same key the markdown widget consumes for in-doc anchors.

ByteOffset mirrors markdown.HeadingInfo.ByteOffset: the heading text's offset within the bytes BookI.Source returns, -1 when the heading has no text. It is what lets a search index (ADR-0164) slice that source into per-section regions without re-parsing.

Directories

Path Synopsis
Package docref is the canonical string form of a documentation reference (ADR-0164 §SD5): the one identifier a search hit carries so any surface — a results row, a query result cell, a launch request — can navigate to the section it names.
Package docref is the canonical string form of a documentation reference (ADR-0164 §SD5): the one identifier a search hit carries so any surface — a results row, a query result cell, a launch request — can navigate to the section it names.
Package search answers pattern-battery queries over a help library's section-grained corpus (ADR-0164 §SD2/§SD3).
Package search answers pattern-battery queries over a help library's section-grained corpus (ADR-0164 §SD2/§SD3).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL