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 ¶
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 ¶
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 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 Books ¶
func Books() (books []BookI)
Books is a package-level shortcut for [DefaultLibrary.Books].
func NewBook ¶
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 `` and Obsidian `![[file.png]]` — arrive as path strings rooted at the FS root. A leading `/` is stripped so `` and `` 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 ¶
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 ¶
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.
type SectionInfo ¶
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). |