Documentation
¶
Overview ¶
Package render renders the public site from layered html/template themes.
A theme is a directory tree (or fs.FS) holding theme.json, layouts/, partials/, sections/<type>.html, schemas/<type>.json and assets/. Files are resolved through a Chain: site override > disk theme > embedded default.
The package is deliberately pure: it never imports the server package. The server builds a PageView from stored entities and hands it to the Engine.
Index ¶
- Constants
- Variables
- func BootFaults() []string
- func CanvasBridgeScriptHash() string
- func CheckPluginFunc(plugin, name string, fn any) error
- func CheckPluginPartial(plugin, name, src string) error
- func DefaultFS() fs.FS
- func DegradedCount() int64
- func EntityLinkIDs(htmlStr string) []string
- func FieldKind(node json.RawMessage) string
- func InjectCanvasBridge(html []byte) []byte
- func NoteBootFault(msg string)
- func NoteDegraded()
- func NoteDegradedCtx(ctx context.Context)
- func PluginMarkupStopped(plugin string) []string
- func PluginSectionOwner(typ string) (string, bool)
- func ReadingMinutes(words int) int
- func ReadingTime(markup string) int
- func RegisterPluginFunc(plugin, name string, fn any) error
- func RegisterPluginPartial(plugin, name, src string) error
- func RegisterPluginSection(plugin, typ, src string) error
- func RemovePlugin(plugin string)
- func ReplaceTextNode(body string, index int, expect, value string) (string, error)
- func RewriteEntityLinks(htmlStr string, hrefs map[string]string) string
- func Robots(origin string, extra []string) string
- func RobotsForAdmin(origin string, extra, denyTokens []string, adminPath string) string
- func RobotsWithDeny(origin string, extra, denyTokens []string) string
- func SetPackWidgets(ws []PackWidget) error
- func SetPluginAssets(plugin, cssURL, jsURL string)
- func SetPluginScriptGate(allowed func(plugin string) bool)
- func SetPublicAPIPrefix(prefix string)
- func SitemapXSL() []byte
- func StampEditableText(body, fieldPath string) string
- func ValidColor(v string) bool
- func ValidFont(v string) bool
- func ValidRadius(v string) bool
- func ValidatePackWidget(kind string, f PackFiles) (err error)
- func WordCount(markup string) int
- type Address
- type AttrCount
- type AuthorLink
- type AuthorView
- type Chain
- type Crumb
- type DataSource
- type DegradeScope
- type DocBody
- type Engine
- func (e *Engine) AssetHandler() http.Handler
- func (e *Engine) BuilderParts() []string
- func (e *Engine) BuilderTokens() bool
- func (e *Engine) CheckTheme(layers ...fs.FS) error
- func (e *Engine) FieldKind(sectionType string, path []string) string
- func (e *Engine) NodeCSS(n *builder.Node) map[string]string
- func (e *Engine) NodeCSSFor(v *PageView, n, parent *builder.Node) map[string]string
- func (e *Engine) OverrideAssets(pick func(*http.Request) http.Handler)
- func (e *Engine) PrefetchExt(v *PageView, nodes []*builder.Node)
- func (e *Engine) RenderError(w io.Writer, v *PageView, status int) error
- func (e *Engine) RenderNode(w io.Writer, v *PageView, n *builder.Node, canvas bool) error
- func (e *Engine) RenderNotFound(w io.Writer, v *PageView) error
- func (e *Engine) RenderPage(w io.Writer, v *PageView) error
- func (e *Engine) RenderTop(v *PageView, n *builder.Node) (Fragment, bool, error)
- func (e *Engine) SectionTypes(layers ...fs.FS) ([]string, error)
- func (e *Engine) SetSettings(m map[string]any)
- func (e *Engine) SetTheme(layers ...fs.FS) error
- func (e *Engine) SetThemeName(name string)
- func (e *Engine) StrictScripts() bool
- func (e *Engine) ThemeInfo() ThemeInfo
- func (e *Engine) Version() string
- type EntityView
- type ExtBatch
- type ExtRequest
- type ExtResult
- type ExtSourceFunc
- type FeedLink
- type FormField
- type FormOption
- type FormView
- type Fragment
- type HeadContext
- type HeadFilter
- type HeadTag
- type ImageSizer
- type ImageVariant
- type Link
- type NavItem
- type NavPosition
- type Options
- type PackFiles
- type PackWidget
- type PageLink
- type PageMeta
- type PageView
- type Pagination
- type PermLine
- type PluginCard
- type PluginDirectoryView
- type PostsPage
- type QueryResult
- type QuerySpec
- type RefSource
- type RelatedItem
- type Section
- type SectionDef
- type SectionInfo
- type SectionMeta
- type SeriesPartView
- type SeriesView
- type SiteView
- type TOCItem
- type TermView
- type TextNode
- type Theme
- type ThemeCard
- type ThemeDirectoryView
- type ThemeInfo
- type ThemeTokens
- type VariantPrefetcher
Constants ¶
const ( KindPage = "page" KindSingle = "single" KindHome = "home" KindArchive = "archive" KindTaxonomy = "taxonomy" KindAuthor = "author" KindSearch = "search" )
Template kinds. A view's Kind picks the layout (see layoutCandidates).
const ( // DefaultQueryLimit is the page size when a query gives none. DefaultQueryLimit = 12 // MaxQueryLimit caps a query's limit. MaxQueryLimit = 100 // MaxQueryPage caps a query's page (and PageNum). MaxQueryPage = 1000 )
Limits of the query func.
const ( PluginDirectoryOK = "ok" PluginDirectoryEmpty = "empty" PluginDirectoryOff = "off" // no registry is configured )
Plugin directory states.
const AssetPrefix = "/theme/assets/"
AssetPrefix is the URL prefix under which AssetHandler serves theme assets.
const BaseCSS = `.hm-c{display:flex;flex-direction:column;box-sizing:border-box;min-width:0}` +
`.hm-c--grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}` +
`.hm-c--page{max-width:var(--hm-container,72rem);margin-inline:auto;padding-inline:var(--hm-space-4,1rem)}` +
`.hm-w-btn{display:inline-block;align-self:flex-start}` +
`.hm-w-img{max-width:100%;height:auto;display:block}` +
`.hm-w-spacer{height:var(--hm-space-8,2rem)}` +
`.hm-w-divider{border:0;border-top:1px solid var(--hm-hair,#ddd);width:100%}`
BaseCSS is the builder's own stylesheet for containers and core widgets. Defaults only; every StyleSpec property overrides through .hm-n-<id>.
const ContractVersion = "1"
ContractVersion is emitted as data-hm-contract on <html>; bump it when the data-hm-* marker contract changes incompatibly (canvas editor depends on it).
const IconDir = "icons/"
IconDir is the theme asset directory holding the site icons the server also answers at the site root (/favicon.ico, /apple-icon.png, ...). A theme overrides them by shipping its own files there.
const IslandPrefix = "builder/"
IslandPrefix is the path below AssetPrefix under which widget islands are served.
const SectionFailedMarker = "<!-- section render failed"
SectionFailedMarker prefixes the HTML comment a failed section degrades to. Callers use it to avoid caching a degraded page.
Variables ¶
var ( ErrStaleText = errors.New("render: text node does not match expected text") ErrBlank = errors.New("render: replacement text is blank") ErrRange = errors.New("render: text node index out of range") )
Errors returned by ReplaceTextNode.
var RootIcons = []string{"favicon.ico", "favicon-16.png", "favicon-32.png", "apple-icon.png", "icon-192.png", "icon-512.png"}
RootIcons are the root paths served from IconDir, without the leading slash.
Functions ¶
func BootFaults ¶
func BootFaults() []string
BootFaults returns the faults recorded since the process started.
func CanvasBridgeScriptHash ¶
func CanvasBridgeScriptHash() string
CanvasBridgeScriptHash returns the CSP hash source ('sha256-...') of the exact script body InjectCanvasBridge places inside the bridge <script> tag, so a CSP can allow the bridge and no other script.
func CheckPluginFunc ¶
CheckPluginFunc reports whether RegisterPluginFunc would accept the func, without registering it, so a host can validate a plugin's registrations before applying any of them.
func CheckPluginPartial ¶
CheckPluginPartial is the RegisterPluginPartial dry run.
func DefaultFS ¶
DefaultFS returns the embedded default theme rooted at its top directory (theme.json, layouts/, partials/, sections/, schemas/, assets/).
func DegradedCount ¶
func DegradedCount() int64
DegradedCount is the number of plugin render failures so far.
func EntityLinkIDs ¶
EntityLinkIDs returns the distinct data-entity-id values of the <a> tags in html, in order of first appearance.
func FieldKind ¶
func FieldKind(node json.RawMessage) string
FieldKind returns the x-hm-field value of a (sub)schema node, or "".
func InjectCanvasBridge ¶
InjectCanvasBridge inserts the canvas bridge <script> as the first thing in <head>, so it runs before any page script and can latch its private port. Without a head it goes after the <html> open tag, then before the last </body>. It is a no-op (returns html unchanged) when the document has none of these or already carries the bridge.
func NoteBootFault ¶
func NoteBootFault(msg string)
NoteBootFault records a release-level render fault (deduplicated, capped).
func NoteDegraded ¶
func NoteDegraded()
NoteDegraded records that a plugin failed during a render. Besides the process-wide counter it marks the DegradeScope bound to the calling goroutine, if any: template funcs run synchronously on the render's own goroutine, so that is the one request whose page lost the output.
func NoteDegradedCtx ¶
NoteDegradedCtx is NoteDegraded for callers holding the request context: it marks the context's DegradeScope, else the goroutine's.
func PluginMarkupStopped ¶
PluginMarkupStopped lists, for the admin, the partials and sections of the plugin that are not registered because they carry script constructs and the plugin does not hold site.scripts. It is empty once the owner grants it.
func PluginSectionOwner ¶
PluginSectionOwner returns the plugin that registered the section type, if a plugin did.
func ReadingMinutes ¶
ReadingMinutes is the reading time of a text of that many words.
func ReadingTime ¶
ReadingTime is the whole minutes needed to read the text of an HTML string: at least 1 for any text, 0 for none.
func RegisterPluginFunc ¶
RegisterPluginFunc registers fn as template func plugin_<plugin>_<name> (a "-" in the plugin name becomes "_": acme-seo/badge is plugin_acme_seo_badge). fn must return one value or (value, error); value types are limited to string, bool, numbers and slices/maps of those. Trusted content types (template.HTML, JS, URL, CSS, HTMLAttr, ...) are rejected so plugin output is always contextually escaped.
func RegisterPluginPartial ¶
RegisterPluginPartial registers an html/template source as the partial "plugin/<plugin>/<name>".
func RegisterPluginSection ¶
RegisterPluginSection registers an html/template source as the section type typ. It defines "section-<typ>" like a theme's sections/<typ>.html and has lower precedence: a theme section of the same type wins.
func RemovePlugin ¶
func RemovePlugin(plugin string)
RemovePlugin drops every func, partial, section and public asset link the plugin registered.
func ReplaceTextNode ¶
ReplaceTextNode is the Go twin of the frontend replaceTextNode (used by tests to pin behaviour): it sets text node index to value when its current text equals expect, and returns the re-serialised body.
func RewriteEntityLinks ¶
RewriteEntityLinks sets the href of every <a data-entity-id="X"> whose X is a key of hrefs. Everything else in html is copied through byte for byte. The hrefs come from the URL registry and are root-relative paths; a value that is not (no leading "/", or "//") is ignored.
func Robots ¶
Robots returns the robots.txt body: allow everything except the admin and API surfaces, plus extra Disallow paths, and an absolute sitemap URL.
func RobotsForAdmin ¶
RobotsForAdmin is RobotsWithDeny for a site whose admin is served at adminPath. The admin Disallow line is written only when adminPath is exactly "/admin": the default is public knowledge, any other address stays out of a file every crawler reads.
func RobotsWithDeny ¶
RobotsWithDeny is Robots plus one "User-agent: <token> / Disallow: /" group per denied token, placed before the Sitemap line exactly like the Next.js route. With no tokens the output is identical to Robots.
func SetPackWidgets ¶
func SetPackWidgets(ws []PackWidget) error
SetPackWidgets replaces the plugin widgets with ws (nil removes them all) in one atomic swap. Every widget is validated again; on error the previous set stays. Kinds are ordered embedded first, then plugin widgets by name.
func SetPluginAssets ¶
func SetPluginAssets(plugin, cssURL, jsURL string)
SetPluginAssets makes pages load the plugin's stylesheet and script (either may be ""). The URLs must be site-relative paths; others are ignored.
func SetPluginScriptGate ¶
SetPluginScriptGate installs the check pages make, on every render, before linking a plugin's script: the owner's site.scripts grant. The stylesheet is not gated.
func SetPublicAPIPrefix ¶
func SetPublicAPIPrefix(prefix string)
SetPublicAPIPrefix makes hm.js call prefix instead of /api/v1/public, and prefix+"/track" instead of /api/v1/track. "" restores the defaults.
func SitemapXSL ¶
func SitemapXSL() []byte
SitemapXSL returns the embedded stylesheet that makes sitemaps readable in a browser, or nil when the theme does not ship one.
func StampEditableText ¶
StampEditableText marks each editable text node of body with data-hm-text="<fieldPath>#<n>". A node that is the only child of a non-root element other than button or summary stamps that element; every other node is wrapped in a <span>. Returns body unchanged on parse/render error or when body is oversized.
func ValidColor ¶
ValidColor reports whether v is empty or a hex/rgb()/hsl()/named colour.
func ValidRadius ¶
ValidRadius reports whether v is empty or a CSS length.
func ValidatePackWidget ¶
Types ¶
type Address ¶
type Address struct {
Street string `json:"street"`
City string `json:"city"`
State string `json:"state"`
PostalCode string `json:"postal_code"`
Country string `json:"country"`
}
Address is the business postal address of the site settings.
type AttrCount ¶
AttrCount is a distinct attribute value and the number of published entities that have it.
type AuthorLink ¶
type AuthorLink struct{ Label, URL string }
AuthorLink is a profile link and the name shown for it.
type AuthorView ¶
type AuthorView struct {
Name, Slug, URL, Avatar string
// Bio is the author's short biography ("" when none).
Bio string
// JobTitle is the person's role ("" when none).
JobTitle string
// IsOrg is true for a byline that credits the site organisation: Name is
// the site name and there is no URL.
IsOrg bool
// SameAs are the person's public profile links (website, LinkedIn, ...).
SameAs []string
// Links are the same links with a visible label ("LinkedIn", "X", ...).
Links []AuthorLink
}
AuthorView is a public author: name, avatar and archive URL only (never an email address).
type Chain ¶
Chain is an ordered lookup over several file systems. Index 0 wins: the conventional order is site override, disk theme, embedded default.
func (Chain) Glob ¶
Glob returns the sorted union of pattern matches across all layers; a name present in several layers appears once (ReadFile resolves its precedence).
type DataSource ¶
type DataSource interface {
Recent(schema string, limit int) []RelatedItem
Terms(kind string, limit int) []TermView
Menu(location string) []NavItem
// ImageVariants returns the resized copies of a local media URL, widest last.
ImageVariants(url string) []ImageVariant
// Query lists published entities matching q (see QuerySpec); the spec the
// funcs pass is already clamped.
Query(q QuerySpec) QueryResult
// AttrValues lists the distinct non-empty string values of an attribute of
// a schema's published entities, sorted by value.
AttrValues(schema, attr string, limit int) []AttrCount
}
DataSource feeds the recent, terms, menu and srcset template funcs. It takes no context: the implementation owns its timeouts and is fail-open (an error yields no items). A nil DataSource yields no data.
type DegradeScope ¶
type DegradeScope struct {
// contains filtered or unexported fields
}
DegradeScope counts the plugin render failures of ONE request, immune to failures that concurrent visitor renders cause. Bind it (BindDegradeScope) on the goroutine that renders the page.
func BindDegradeScope ¶
func BindDegradeScope(ctx context.Context) (context.Context, *DegradeScope, func())
BindDegradeScope returns a scope for the calling goroutine's render and a context carrying it. Call release when the render is done.
func (*DegradeScope) Count ¶
func (s *DegradeScope) Count() int64
Count is the failures this scope's request has seen.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine renders pages from a layered theme. It is safe for concurrent use.
func (*Engine) AssetHandler ¶
AssetHandler serves /theme/assets/* for the current theme version.
func (*Engine) BuilderParts ¶
BuilderParts lists the layout slots the live theme opted in to render builder parts for (theme.json builder.parts), in display order.
func (*Engine) BuilderTokens ¶
BuilderTokens reports whether the live theme opted in to the site design tokens (theme.json builder.tokens).
func (*Engine) CheckTheme ¶
CheckTheme reports why layers could not serve pages: a template that does not parse, a missing required template, or one that fails against a fixture page of every layout kind. It changes nothing.
func (*Engine) FieldKind ¶
FieldKind returns the x-hm-field kind of the content field at path (object keys only, array indices skipped) for a section type of the current theme, or "" when the type or field is not in its schema. The server registers it with entity.SetFieldKindResolver so save-time sanitising follows the schema.
func (*Engine) NodeCSS ¶
NodeCSS compiles the styles of the top-level node n and its descendants, in id order, for the per-node <style data-hm-css> tags of a canvas patch. It is canvas-only: the rules also carry the forced state selectors.
func (*Engine) NodeCSSFor ¶
NodeCSSFor is NodeCSS for a node of the page v under parent (nil at top level): a ref inside n is expanded first, so the CSS of the block's nodes is included, and parent names the element its parent-hover rules select on.
func (*Engine) OverrideAssets ¶
OverrideAssets lets pick return another handler for a request's theme assets (nil from pick: serve the engine's own). Used by the theme preview.
func (*Engine) PrefetchExt ¶
PrefetchExt fetches the ext data of every node (refs expanded) in one batch, the way a page render does. A caller that renders several top nodes of one view calls it first, so each RenderTop finds its data already asked instead of spending one of the two plugin round trips a view may make.
func (*Engine) RenderError ¶
RenderError renders layouts/error.html for an HTTP error status.
func (*Engine) RenderNode ¶
RenderNode writes the markup of one node subtree: a fragment, not a page. It runs no layout, head or section preparation, so the canvas can patch a node without rendering the page around it. Hidden nodes write nothing.
func (*Engine) RenderNotFound ¶
RenderNotFound renders layouts/404.html (noindex, no sections).
func (*Engine) RenderPage ¶
RenderPage renders the layout the template hierarchy picks for v (see layoutCandidates). A view with no Kind is a single page when it has an entity (v.EntityID set) and a plain page otherwise.
func (*Engine) RenderTop ¶
RenderTop renders top-level node n exactly as docBlocks does inside a page: a v1-shaped node through the theme section partial, anything else natively. Canvas markers are always on. ok is false when the node renders nothing (hidden or blank), so a full render would not contain it either.
func (*Engine) SectionTypes ¶
SectionTypes returns the section types a theme made of layers would render: its own, the built-in default's and installed plugins', sorted.
func (*Engine) SetSettings ¶
SetSettings replaces the theme settings templates read as .Settings.
func (*Engine) SetTheme ¶
SetTheme switches the engine to a new set of theme layers without a restart. A theme that does not compile is refused and the current one keeps serving. A theme that compiles but cannot render is accepted like at boot (see compile); use CheckTheme first to learn about that.
func (*Engine) SetThemeName ¶
SetThemeName names the theme the next SetTheme applies, so a fault it causes has a stable identifier.
func (*Engine) StrictScripts ¶
StrictScripts reports whether the live theme opted in to the public policy without inline scripts (theme.json csp.strictScripts).
type EntityView ¶
type EntityView struct {
ID, Title, Slug, URL, Schema string
// Excerpt and Image are the first of the common attribute spellings.
Excerpt, Image string
// Attributes is a read-only copy of the stored attributes (page_sections omitted).
Attributes map[string]any
Terms []TermView
Author *AuthorView
PublishedAt time.Time
UpdatedAt time.Time
}
EntityView is the whitelisted view of the entity a page shows.
func (*EntityView) Categories ¶
func (e *EntityView) Categories() []TermView
Categories are the entity's terms of kind "category".
func (*EntityView) Tags ¶
func (e *EntityView) Tags() []TermView
Tags are the entity's terms of kind "tag".
type ExtBatch ¶
type ExtBatch struct {
Path, Language, SchemaSlug, EntityID string
Requests []ExtRequest
}
ExtBatch is every external-source request of one page render, sent in a single call so a render never costs more than one plugin round trip.
type ExtRequest ¶
ExtRequest asks a plugin data source for one value or item list. ID is unique within a batch; Source is "ext.<plugin>.<name>"; Limit bounds the items of an items source.
type ExtResult ¶
type ExtResult struct {
Value string
Items []RelatedItem
OK bool
}
ExtResult answers one ExtRequest: Value for a value source, Items for an items source. OK is false for a request nobody answered or that failed.
type ExtSourceFunc ¶
ExtSourceFunc resolves a batch and returns the results by request ID. A request that is absent or has OK false renders empty. Nil-safe: a nil func means no external data.
type FeedLink ¶
type FeedLink struct {
Title, Type, Href string
}
FeedLink is one advertised feed: Type is its MIME type, Href its URL.
type FormField ¶
type FormField struct {
Name string
Label string
Type string // text|email|tel|textarea|select|checkbox|radio
Required bool
Placeholder string
HelpText string
Options []FormOption
}
FormField is one input of a FormView.
type FormOption ¶
FormOption is one choice of a select or radio field.
type FormView ¶
type FormView struct {
Slug string
Name string
Fields []FormField
SubmitLabel string
SuccessNotice string
// Captcha asks for a spam-check widget above the submit button. It is set
// only when the form requires the check and a provider is usable.
// CaptchaSiteKey is public by design; the secret never reaches a view.
Captcha bool
CaptchaProvider string // turnstile|recaptcha|hcaptcha
CaptchaSiteKey string
}
FormView is a public form definition used by contact_form sections.
type Fragment ¶
type Fragment struct {
Sel string `json:"sel"` // "section" | "node"
Key string `json:"key"`
HTML string `json:"html"`
CSS map[string]string `json:"css,omitempty"`
}
Fragment is one rendered node for a canvas patch.
type HeadContext ¶
type HeadContext struct {
Path string
Language string
SchemaSlug string
EntityID string
Tags []HeadTag
// ReplaceSiteGraph asks the host to omit its generic Organization/WebSite
// graph because the filter supplies its own as jsonld tags.
ReplaceSiteGraph bool
}
HeadContext is the render.head filter input/output.
type HeadFilter ¶
type HeadFilter func(ctx context.Context, c HeadContext) HeadContext
HeadFilter transforms the head context; it is fail-open (see applyHeadFilter).
type HeadTag ¶
type HeadTag struct {
// Kind is meta, link, script-src or jsonld.
Kind string
// Attrs are the tag attributes; names outside the per-kind allowlist are dropped.
Attrs map[string]string
// JSONLD is the document for Kind "jsonld".
JSONLD []byte
}
HeadTag is one tag a render.head filter asks the host to emit. The host builds and escapes the markup; a filter never supplies raw HTML.
type ImageSizer ¶
ImageSizer is an optional DataSource extension: ImageSize returns the intrinsic pixel size of a local media URL, which an image writes as its width and height attributes so the browser reserves the space (no layout shift). ok is false for unknown, external or size-less files; the image then has no size attributes.
type ImageVariant ¶
ImageVariant is one resized copy of an image.
type Options ¶
type Options struct {
// Layers are theme file systems layered over the embedded default, highest
// precedence first. The server passes the active theme package (loaded from
// Postgres); none means the built-in default theme alone.
Layers []fs.FS
// Settings are the active theme's settings values, exposed to templates as
// .Settings (see SetSettings).
Settings map[string]any
// HeadFilter optionally lets plugins add head tags (fail-open).
HeadFilter HeadFilter
// Data feeds the recent, terms, menu and srcset template funcs (optional).
Data DataSource
// ThemeName names the theme in boot fault identifiers (see SetThemeName).
ThemeName string
// Refs resolves ref nodes (synced blocks) and theme parts. Without it a ref
// renders as a missing block.
Refs RefSource
// ExtSources resolves plugin data sources for ext.* bindings and source
// loops, one batch per render (nil: no external data).
ExtSources ExtSourceFunc
// Logger receives rebuild and plugin partial diagnostics (default slog.Default()).
Logger *slog.Logger
}
Options configures an Engine.
type PackFiles ¶
type PackFiles struct {
HTML, CSS, Island []byte
}
PackFiles are the files of one plugin widget, as ValidatePackWidget reads them.
type PackWidget ¶
PackWidget is the view side of one plugin widget: the files of widgets/<name>/. Kind is the full kind (<plugin>_<name>).
type PageLink ¶
type PageLink struct {
N int
URL string
Current bool
// Gap marks an ellipsis between non-adjacent pages (N and URL are empty).
Gap bool
}
PageLink is one entry of the paginate func: a numbered page or a gap.
type PageMeta ¶
type PageMeta struct {
Name string
Title string
Description string
OGImage string
OGType string
CanonicalURL string
NoIndex bool
}
PageMeta is the SEO metadata of the page being rendered.
type PageView ¶
type PageView struct {
// Ctx is the request context, used only so render failures are logged with
// it (the smoke check attributes errors by it). Nil means no request.
Ctx context.Context
// Kind selects the template hierarchy (see layoutCandidates). Empty means
// KindSingle for an entity page (EntityID set) and KindPage otherwise.
Kind string
// Entity is the entity behind a single or home view; nil elsewhere.
Entity *EntityView
// Items and Pagination are set on archive, taxonomy, author and search views.
Items []RelatedItem
Pagination *Pagination
// Term is set on taxonomy views, Author on author views, Query on search views.
Term *TermView
Author *AuthorView
Query string
// SearchType is the schema slug a search is limited to ("" for the whole site).
SearchType string
// Dynamic marks a view built from per-request data (taxonomy, author,
// search) that no entity event invalidates: the server never caches it.
Dynamic bool
Site SiteView
Page PageMeta
Sections []Section
// Doc is a page_doc v2 tree. When set it replaces Sections: v1-shaped nodes
// render through the theme section partial, the rest natively.
Doc *builder.Doc
BaseURL string
Path string
Breadcrumb []Crumb
// JSONLD holds backend-built JSON-LD documents, emitted verbatim (escaped).
JSONLD []json.RawMessage
// AICrawlerComment is an optional HTML comment body for AI crawlers.
AICrawlerComment string
EntityID string
SchemaSlug string
// Attrs is a read-only copy of the entity's attributes (page_sections
// omitted; it is in Sections). Numbers are float64. Entity pages only. Treat strings as
// plain text: html/template escapes them; use sanitisedHTML for rich text.
Attrs map[string]any
// EntityLinks maps the data-entity-id of internal links in the page content to
// the target's current public path. RewriteEntityLinks applies it at render so
// links survive slug changes; ids missing from the map keep their stored href.
EntityLinks map[string]string
// PageNum is the request's ?page= (1..MaxQueryPage, 0 reads as 1); the
// pageNum func reads it on every view.
PageNum int
// Params is the request's query string; the queryParam func reads it on
// every view. Pages with a query string are never page-cached.
Params url.Values
// Related lists up to limit (clamped to 1..12) other published entities of
// the page's schema, newest first, excluding the page itself. Lazy: the
// query only runs when a layout calls it ({{range call .Related 3}}). Never
// nil in a layout (a no-op returning nil on archive, 404, error and
// unsaved-preview views, and when nothing is found).
Related func(limit int) []RelatedItem
// Series returns the editorial series the entity belongs to
// (attributes.series_slug): its published and scheduled parts in part
// order, or nil when the entity has no series or it has no parts. Lazy and memoised per
// render ({{with call .Series}}...{{end}}). Never nil in a layout.
Series func() *SeriesView
// PostsByAuthor lists a page of the published posts written by this
// team-member entity, newest first. limit is clamped to 1..50 and page to
// >= 1 (pass .PageNum). Lazy and memoised per (limit, page). Never nil in a
// layout: a zero PostsPage on any other entity.
PostsByAuthor func(limit, page int) PostsPage
// Uncacheable, when non-nil, is set during rendering if a lazy data source
// (Related) failed, so the caller serves the page but does not cache it.
Uncacheable *bool
// PagedLoop and LoopOutOfRange, when non-nil, are set during rendering when
// the page has a paged builder loop and when the requested ?page= is past
// its last page. The server caches ?page=N only for the former and answers
// 404 for the latter.
PagedLoop, LoopOutOfRange *bool
// PublishedAt and UpdatedAt are zero when unknown.
PublishedAt time.Time
UpdatedAt time.Time
// Forms is keyed by form slug, filled for each contact_form section.
Forms map[string]FormView
// PluginDirectory feeds plugin_directory sections; nil when the page has
// none.
PluginDirectory *PluginDirectoryView
// ThemeDirectory feeds theme_directory sections; nil when the page has
// none.
ThemeDirectory *ThemeDirectoryView
// MaxCacheAge, when > 0, caps how long the rendered page may be cached,
// whatever the configured page cache TTL.
MaxCacheAge time.Duration
// Preview marks an unsaved-draft render (noindex, data-hm-preview).
Preview bool
// Canvas marks an admin canvas render: html-kind bodies carry data-hm-text
// editor markers (see canvas_text.go).
Canvas bool
// NoParts turns the theme parts off for this render: the canvas of a part
// itself, so the assigned part is not drawn twice.
NoParts bool
// Submitted is set after a no-JS form post redirect (?submitted=1).
Submitted bool
// FormError is set after a failed no-JS form post redirect (?form_error=1).
FormError bool
// contains filtered or unexported fields
}
PageView is everything a layout needs to render one page. The server builds it from stored entities; the render package never touches the database.
type Pagination ¶
type Pagination struct {
Page, PerPage, Total, TotalPages int
HasPrev, HasNext bool
PrevURL, NextURL string
// Path and Query (the other, already encoded parameters) build page URLs.
Path string
Query string
}
Pagination describes one page of a listing. URLs are root-relative and keep the other query parameters (?page=N, page 1 has no parameter).
func NewPagination ¶
func NewPagination(path string, query url.Values, page, perPage, total int) *Pagination
NewPagination describes page of a listing of total items, perPage per page. path is the listing's path; query holds the parameters to keep (any "page" entry is ignored). page is clamped to 1..TotalPages.
func (*Pagination) URL ¶
func (p *Pagination) URL(n int) string
URL is the root-relative URL of page n; page 1 carries no page parameter.
type PermLine ¶ added in v1.0.2
PermLine is one permission of a plugin card: its name, the plain-English text and whether it is a risky one.
type PluginCard ¶
type PluginCard struct {
Name, Title, Version, Tier, TierLabel, Author, Description, Source string
PermCount int
PermWarn []string
PermLines []PermLine
// Permissions lists the raw permission names. The default theme no longer
// reads it; it is kept for theme overrides written against older cores.
Permissions []string
Search string
}
PluginCard is one plugin entry of the directory. Source is already restricted to safe https URLs; Search is the lowercased text the search box matches against.
PermCount is the number of permissions the plugin asks for, PermWarn the short labels of the risky ones (empty: no warning badge) and PermLines the plain-English lines shown in the collapsed list. Permissions holds the raw names; the default theme no longer reads it, it is kept for theme overrides written against older cores.
type PluginDirectoryView ¶
type PluginDirectoryView struct {
State string
Cards []PluginCard
}
PluginDirectoryView is the verified registry's plugin list for plugin_directory sections. State is one of the PluginDirectory* constants.
type PostsPage ¶
type PostsPage struct {
Items []RelatedItem
// Total counts every matching post; Page is the page served; Pages is the
// page count (at least 1).
Total, Page, Pages int
}
PostsPage is one page of PostsByAuthor.
type QueryResult ¶
type QueryResult struct {
Items []RelatedItem
Total, Page, Pages, Limit int
}
QueryResult is one page of a query. Pages is at least 1; Page is clamped to Pages. Items is empty (never an error) when nothing matches or a lookup failed.
type QuerySpec ¶
type QuerySpec struct {
// Schema is the schema slug (aliases are included by the implementation).
Schema string
// Attr and Eq filter on attributes[Attr] == Eq; Attr alone requires the
// attribute to exist. EqSet says Eq was given (an empty Eq then matches
// the empty string, not "exists").
Attr, Eq string
EqSet bool
// IncludeScheduled adds scheduled entities (their date, no URL).
IncludeScheduled bool
// Author is an author slug, Term a "<kind>:<slug>" taxonomy term. A slug
// that names nothing gives no items.
Author, Term string
// Order is "-published_at" (default), "published_at", "name", "-name",
// "attr:<key>" or "-attr:<key>".
Order string
// Limit is 1..MaxQueryLimit and Page 1..MaxQueryPage.
Limit, Page int
// Offset skips this many items before the first page (0..1000).
Offset int
// ExcludeID is the uuid (text) of an entity to leave out; an invalid
// value is ignored.
ExcludeID string
}
QuerySpec is what the query func asks of a DataSource. Every field but Schema is optional ("" = no filter).
type RefSource ¶
type RefSource interface {
// Block returns the doc of a block entity. It is false for an unknown id
// and for an entity that is not a block (a page id, a part id); the status
// does not matter, blocks are always drafts.
Block(ctx context.Context, id string) (*builder.Doc, bool)
// Part returns the doc of a theme_part entity whose slot is slot.
Part(ctx context.Context, slot, id string) (*builder.Doc, bool)
}
RefSource resolves the entities a doc embeds by reference. The server implements it on the entity store; render never imports entity.
type RelatedItem ¶
type RelatedItem struct {
Title, URL, Excerpt, Image, Category string
// Name is the entity's own name (Title is attributes.title, else Name).
Name string
// Schema is the slug of the entity's schema.
Schema string
PublishedAt time.Time
// UpdatedAt is zero for scheduled items.
UpdatedAt time.Time
// Author and AuthorURL are empty when the entity has no named author.
Author, AuthorURL string
// WordCount is the number of words of the entity's attributes.content and
// ReadingTime the minutes to read it, both as the readingTime func counts
// (0 when there is no content).
WordCount, ReadingTime int
// Attrs is a read-only copy of the entity's attributes without
// page_sections and content (the body would make every card heavy). Numbers
// are float64; strings are plain text that html/template escapes.
Attrs map[string]any
}
RelatedItem is the whitelisted view of a listed entity (recent, related, query and archive/taxonomy/author/search items).
type Section ¶
type Section struct {
ID string
Type string
Title string
Content map[string]any
IsVisible *bool
SortOrder int
}
Section is one stored page section, as in attributes.page_sections[].
type SectionDef ¶
type SectionDef struct {
Type string
// Template is the html/template source of sections/<type>.html.
Template string
// Schema is the raw JSON Schema of schemas/<type>.json (nil if absent).
Schema json.RawMessage
// Meta is the schema's x-hm-section extension.
Meta SectionMeta
// Aliases maps a canonical top-level field to its legacy names (x-hm-alias).
Aliases map[string][]string
// FieldKinds maps a top-level field to its x-hm-field kind.
FieldKinds map[string]string
}
SectionDef is one section type of a theme: its template source and schema.
type SectionInfo ¶
type SectionInfo struct {
Type string `json:"type"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
Schema json.RawMessage `json:"schema,omitempty"`
}
SectionInfo describes one section type for the admin schema form.
type SectionMeta ¶
type SectionMeta struct {
Title string `json:"title"`
Description string `json:"description,omitempty"`
Icon string `json:"icon,omitempty"`
}
SectionMeta is the top-level x-hm-section extension of a section schema.
type SeriesPartView ¶
type SeriesPartView struct {
Title, URL string
Part int
Index bool
Published bool
PublishDate time.Time
}
SeriesPartView is one published post in a series.
type SeriesView ¶
type SeriesView struct {
Slug, Name string
// Total is the number of numbered parts (the index page is not one).
Total int
IndexURL string
// Parts is ordered by Part; the index page has Part 0 and Index true.
// IndexURL is empty until the index page is published.
Parts []SeriesPartView
}
SeriesView is an editorial series, as GET /api/v1/public/series/{slug} served it. Scheduled parts are listed (Published false, empty URL) so an index can show what is coming; only published parts carry a URL, so a link can never point at a post that is not live.
type SiteView ¶
type SiteView struct {
Name string
Tagline string
LogoURL string
SocialLinks []Link
// HeaderCTA is the optional header button; nil renders none.
HeaderCTA *Link
// Feeds are the site's syndication feeds, advertised in the head as
// <link rel="alternate">. Empty when the site has none.
Feeds []FeedLink
Tokens ThemeTokens
// Language is the BCP 47 site language (default "en" when unset).
Language string
// Phone, Hours and Address are the public contact details from the site
// settings; empty when unset. They also feed the Organization JSON-LD.
Phone, Hours string
Address Address
// Settings are the site's theme settings (theme_config extras), read-only.
Settings map[string]any
// Design is the site design system (tokens and style presets) read from the
// theme settings; the zero value means none.
Design builder.Design
}
SiteView is the site-wide data shared by every page.
type TermView ¶
TermView is a taxonomy term (kind "category", "tag" or a custom kind). Count is 0 unless a listing fills it. Parent is the slug of the parent term ("" for a root) and Description its optional text; URL is the term's archive path, which for a nested term runs through its ancestors' slugs.
type TextNode ¶
type TextNode struct {
Index int `json:"index"`
Tag string `json:"tag"` // parent element name
Text string `json:"text"`
}
TextNode is one editable text node in walk order.
func EditableTextNodes ¶
EditableTextNodes lists the editable text nodes of body in walk order.
type Theme ¶
type Theme struct {
Name string
Version string
// Settings is the raw settings_schema from theme.json (may be nil).
Settings json.RawMessage
// Sections is keyed by section type, discovered from the file layout.
Sections map[string]*SectionDef
// BuilderTokens is the theme's opt-in (theme.json "builder": {"tokens": true})
// to the site design tokens: the :root token block and the --hm-fs-* type
// scale variables. Without it the builder emits neither.
BuilderTokens bool
// BuilderParts are the layout slots (header, footer, single, archive) the
// theme lets the owner fill with a part built in the builder
// (theme.json builder.parts); nil when it did not opt in.
BuilderParts map[string]bool
// StrictScripts is the theme's opt-in (theme.json "csp": {"strictScripts": true})
// to the public policy without 'unsafe-inline' in script-src. It promises the
// theme emits no inline executable script and no on*= handler on public pages.
StrictScripts bool
// contains filtered or unexported fields
}
Theme is a loaded, validated theme resolved through a Chain.
func LoadTheme ¶
LoadTheme resolves theme.json (highest-precedence copy) and discovers section types from sections/*.html and schemas/*.json across the chain. There is no central type list. A schema without a template is an error.
func (*Theme) AssetHandler ¶
AssetHandler serves the theme's assets under AssetPrefix: the fingerprinted CSS bundle (immutable cache), hm.js and any other file below assets/ from the lookup chain (5 minute cache). Unknown paths and directories are 404.
func (*Theme) VersionHash ¶
VersionHash returns a short content hash over the name and bytes of every effective file in the chain (precedence applied), so it changes whenever any layer's visible output could change.
type ThemeCard ¶
type ThemeCard struct {
Name, Title, Version, Description, Source, Screenshot, Tier, TierLabel, Author, Search string
}
ThemeCard is one theme entry of the directory. Source and Screenshot are already restricted to safe https URLs; Search is the lowercased text the search box matches against.
type ThemeDirectoryView ¶
ThemeDirectoryView is the verified registry's theme list for theme_directory sections. State is one of the PluginDirectory* constants.
type ThemeInfo ¶
type ThemeInfo struct {
Name string `json:"name"`
Version string `json:"version"`
Sections []SectionInfo `json:"sections"`
SettingsSchema json.RawMessage `json:"settings_schema,omitempty"`
}
ThemeInfo is the GET /api/v1/render/theme payload.
type ThemeTokens ¶
type ThemeTokens struct {
Primary, Background, Text string
Font, FontHeading string
RadiusSm, RadiusMd, RadiusLg string
}
ThemeTokens are the site theme_config values mapped onto --hm-* variables. Empty values fall back to the theme's own defaults.
type VariantPrefetcher ¶
type VariantPrefetcher interface {
PrefetchVariants(urls []string)
}
VariantPrefetcher is an optional DataSource extension: PrefetchVariants loads the image variants of urls in one lookup, so the ImageVariants calls that follow are served from the cache. Fail-open, no result.
Source Files
¶
- assets.go
- bind.go
- bootfault.go
- canvas.go
- canvas_csp.go
- canvas_text.go
- chain.go
- css.go
- csstokens.go
- datafuncs.go
- degraded.go
- doc.go
- docnav.go
- doctoc.go
- embed.go
- engine.go
- extdata.go
- funcs.go
- funcs_extra.go
- headext.go
- links.go
- markers.go
- node.go
- node_render.go
- packtmpl.go
- pagination.go
- parts.go
- pipeline.go
- pluginassets.go
- pluginmarkup.go
- pluginreg.go
- pluginscripts.go
- refs.go
- robots.go
- schema.go
- seo.go
- smoke.go
- theme.go
- view.go
- widget_accordion.go
- widget_buttons.go
- widget_columns.go
- widget_data.go
- widget_gallery.go
- widget_icon.go
- widget_loop.go
- widget_media.go
- widget_stat.go
- widget_text.go
- widgetpack.go
- widgetpack_sanitize.go
- widgets.go
- widgets_css.go
- widgets_island.go