config

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Jun 27, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BoolPtr

func BoolPtr(v bool) *bool

BoolPtr returns a pointer to the given bool value.

func BoolVal

func BoolVal(p *bool, fallback bool) bool

BoolVal dereferences a *bool, returning fallback if nil.

func NormalizeBasePath

func NormalizeBasePath(raw string) string

NormalizeBasePath canonicalizes a user-supplied base path to the internal form. The canonical form always has a leading and trailing slash (e.g. "/docs/"), except the root case which is just "/".

""            -> "/"
"/"           -> "/"
"docs"        -> "/docs/"
"/docs"       -> "/docs/"
"docs/"       -> "/docs/"
"/docs/"      -> "/docs/"
"  /docs/  "  -> "/docs/"
"//docs//"    -> "/docs/"
"/a/b/"       -> "/a/b/"
"a//b"        -> "/a/b/"
"///"         -> "/"

func Validate

func Validate(cfg *SiteConfig, knownPlugins []string) (errs []validate.Error, warns []validate.Error)

Validate checks the merged SiteConfig for invalid values. knownPlugins is the set of valid plugin names, collected by the build layer from the actual plugin registries. If nil, plugin name validation is skipped.

func ValidateVersioning

func ValidateVersioning(colName string, vc *VersioningConfig) error

ValidateVersioning checks that a versioning config is self-consistent: LastVersion must appear in Versions[].ID, and version IDs must be unique.

Types

type AnalyticsSettings

type AnalyticsSettings struct {
	Provider string `yaml:"provider"`
	SiteID   string `yaml:"site_id"`
	Script   string `yaml:"script"`
}

type BuildSettings

type BuildSettings struct {
	Output   string `yaml:"output"`
	BasePath string `yaml:"base_path"`
	Clean    *bool  `yaml:"clean"`
	Sitemap  *bool  `yaml:"sitemap"`
	Minify   *bool  `yaml:"minify"`
	// LastUpdated selects the strategy for page "last updated" timestamps:
	//   "git"   — `git log -1` for the file, fall back to mtime on error
	//   "mtime" — filesystem modification time (default)
	//   "false" / "off" — disabled; no timestamp rendered
	// Legacy YAML bool form is accepted: true → "mtime", false → "false" (with deprecation warning).
	LastUpdated LastUpdatedStrategy `yaml:"last_updated"`
	Feed        *bool               `yaml:"feed"`
	Drafts      *bool               `yaml:"drafts"`
	Future      *bool               `yaml:"future"`
	Expired     *bool               `yaml:"expired"`
	Parallel    *bool               `yaml:"parallel"`
	Cache       *bool               `yaml:"cache"`
}

type CodeblocksSettings

type CodeblocksSettings struct {
	Engine           string `yaml:"engine"`
	Style            string `yaml:"style"`
	LightTheme       string `yaml:"light_theme"`
	DarkTheme        string `yaml:"dark_theme"`
	Theme            string `yaml:"theme"`
	DarkModeSelector string `yaml:"dark_mode_selector"`
}

type CollectionPrevNextConfig

type CollectionPrevNextConfig struct {
	Enabled *bool    `yaml:"enabled"`
	Labels  []string `yaml:"labels"`
}

type CollectionSidebarConfig

type CollectionSidebarConfig struct {
	Collapsible        *bool `yaml:"collapsible"`
	CollapsedByDefault *bool `yaml:"collapsed_by_default"`
	MaxDepth           int   `yaml:"max_depth"`
	Search             *bool `yaml:"search"`
}

type CollectionSiteConfig

type CollectionSiteConfig struct {
	Enabled      *bool                     `yaml:"enabled"`
	Path         string                    `yaml:"path"`
	URLPrefix    string                    `yaml:"url_prefix"`
	Sort         string                    `yaml:"sort"`
	Layout       string                    `yaml:"layout"`
	Permalink    string                    `yaml:"permalink"`
	Paginate     int                       `yaml:"paginate"`
	Feed         *bool                     `yaml:"feed"`
	Tabs         *bool                     `yaml:"tabs"`
	Sidebar      *CollectionSidebarConfig  `yaml:"sidebar"`
	TOC          *CollectionTOCConfig      `yaml:"toc"`
	PrevNext     *CollectionPrevNextConfig `yaml:"prev_next"`
	Versioning   *VersioningConfig         `yaml:"versioning"`
	I18nFallback string                    `yaml:"i18n_fallback"` // "" (inherit site), "default", or "omit"
}

type CollectionTOCConfig

type CollectionTOCConfig struct {
	Enabled         *bool `yaml:"enabled"`
	Depth           int   `yaml:"depth"`
	ScrollHighlight *bool `yaml:"scroll_highlight"`
}

type ContentLintRules

type ContentLintRules struct {
	HeadingMaxLength    int      `yaml:"heading_max_length"`
	HeadingIncrement    *bool    `yaml:"heading_increment"`
	ImageAltRequired    *bool    `yaml:"image_alt_required"`
	NoEmptyLinks        *bool    `yaml:"no_empty_links"`
	FrontmatterRequired []string `yaml:"frontmatter_required"`
}

type ContentLintSettings

type ContentLintSettings struct {
	Enabled *bool            `yaml:"enabled"`
	Rules   ContentLintRules `yaml:"rules"`
}

type ContentSettings

type ContentSettings struct {
	Dir           string `yaml:"dir"`
	SummaryLength int    `yaml:"summary_length"`
}

type DeployConfig

type DeployConfig struct {
	Provider       string `yaml:"provider"`        // github, netlify, cloudflare, vercel, custom
	Branch         string `yaml:"branch"`          // GitHub Pages branch (default: gh-pages)
	SiteID         string `yaml:"site_id"`         // Netlify site ID
	ProjectName    string `yaml:"project_name"`    // Cloudflare Pages project name
	ProjectID      string `yaml:"project_id"`      // Vercel project ID
	Command        string `yaml:"command"`         // Custom deploy command
	RedirectFormat string `yaml:"redirect_format"` // html, netlify, vercel, all (default: all)
}

type ExternalCheckSettings

type ExternalCheckSettings struct {
	Check       *bool    `yaml:"check"`       // default: false
	Concurrency int      `yaml:"concurrency"` // default: 8
	Timeout     string   `yaml:"timeout"`     // default: "10s" (parsed with time.ParseDuration)
	Cache       string   `yaml:"cache"`       // default: ".sarde/linkcache.json"
	CacheTTL    string   `yaml:"cache_ttl"`   // default: "72h"
	OnBroken    string   `yaml:"on_broken"`   // "warn" (default) | "error" | "ignore"
	Ignore      []string `yaml:"ignore"`      // URL glob patterns to skip
	Method      string   `yaml:"method"`      // "head-then-get" (default) | "head" | "get"
}

type FooterSettings

type FooterSettings struct {
	Text    string    `yaml:"text"`
	Links   []NavLink `yaml:"links"`
	Credits *bool     `yaml:"credits"`
}

type HeadSettings

type HeadSettings struct {
	Tags      []HeadTag `yaml:"tags"`
	CustomCSS []string  `yaml:"custom_css"`
	CustomJS  []string  `yaml:"custom_js"`
}

type HeadTag

type HeadTag struct {
	Tag     string            `yaml:"tag"`
	Attrs   map[string]string `yaml:"attrs"`
	Content string            `yaml:"content"`
}

type HeaderSettings

type HeaderSettings struct {
	Search      *bool     `yaml:"search"`
	ThemeToggle *bool     `yaml:"theme_toggle"`
	Social      *bool     `yaml:"social"`
	Links       []NavLink `yaml:"links"`
}

type HeroCTA

type HeroCTA struct {
	Label string `yaml:"label"`
	URL   string `yaml:"url"`
}

type HeroCode

type HeroCode struct {
	Title    string `yaml:"title"`
	Language string `yaml:"language"`
	Body     string `yaml:"body"`
}

type HeroImageSettings

type HeroImageSettings struct {
	Src   string `yaml:"src"`
	Light string `yaml:"light"`
	Dark  string `yaml:"dark"`
	Alt   string `yaml:"alt"`
	HTML  string `yaml:"html"`
}

type HeroSettings

type HeroSettings struct {
	Eyebrow      string             `yaml:"eyebrow"`
	Title        string             `yaml:"title"`
	Subtitle     string             `yaml:"subtitle"`
	CTA          *HeroCTA           `yaml:"cta"`
	SecondaryCTA *HeroCTA           `yaml:"secondary_cta"`
	Stats        []HeroStat         `yaml:"stats"`
	Code         *HeroCode          `yaml:"code"`
	Image        *HeroImageSettings `yaml:"image"`
	Background   string             `yaml:"background"`
}

type HeroStat

type HeroStat struct {
	Value string `yaml:"value"`
	Label string `yaml:"label"`
}

type HomepageSettings

type HomepageSettings struct {
	Template string       `yaml:"template"`
	Hero     HeroSettings `yaml:"hero"`
}

type I18nSettings

type I18nSettings struct {
	DefaultLanguage string                    `yaml:"default_language"`
	Strategy        string                    `yaml:"strategy"` // "prefix-except-default" (default)
	Fallback        string                    `yaml:"fallback"` // "default" | "omit"
	Strict          bool                      `yaml:"strict"`
	Languages       map[string]LanguageConfig `yaml:"languages"`
}

func (*I18nSettings) GetDefaultLanguage

func (s *I18nSettings) GetDefaultLanguage() string

GetDefaultLanguage returns the configured default language code, or "en" if unset.

func (*I18nSettings) IsLanguageCode

func (s *I18nSettings) IsLanguageCode(seg string) bool

IsLanguageCode reports whether seg is a registered language code.

func (*I18nSettings) IsMultiLang

func (s *I18nSettings) IsMultiLang() bool

IsMultiLang returns true when the site has multiple languages configured.

func (*I18nSettings) Language

func (s *I18nSettings) Language(code string) (LanguageConfig, bool)

Language returns the config for a language code, or false if not found.

func (*I18nSettings) LanguageCodes

func (s *I18nSettings) LanguageCodes() []string

LanguageCodes returns all configured language codes sorted by weight then alphabetically.

func (*I18nSettings) ResolveLang

func (s *I18nSettings) ResolveLang(lang string) string

ResolveLang returns the default language code when lang is empty, otherwise lang.

type IconSet

type IconSet struct {
	Prefix string `yaml:"prefix"`
	File   string `yaml:"file"`
}

IconSet names an extra Iconify JSON collection to load, by prefix, from a file path (relative to the project root).

type IconSettings

type IconSettings struct {
	DefaultPrefix string    `yaml:"default_prefix"`
	Sets          []IconSet `yaml:"sets"`
	SetsDir       string    `yaml:"sets_dir"`
	LocalDir      string    `yaml:"local_dir"`
	Attribution   string    `yaml:"attribution"`
	// Render selects the icon output mode: "inline" (default) emits a full SVG
	// per use; "sprite" emits one hidden <symbol> per unique icon per page and
	// references it with <use>.
	Render string `yaml:"render"`
}

IconSettings configures the SVG icon system: the default set used for bare (prefixless) names, extra Iconify sets to load, a directory of local *.svg files (resolved before any set), an attribution line for sets that require one, and the output render mode.

type ImageSettings

type ImageSettings struct {
	Widths      []int    `yaml:"widths"`
	Formats     []string `yaml:"formats"`
	Quality     int      `yaml:"quality"`
	Placeholder string   `yaml:"placeholder"`
	MaxWidth    int      `yaml:"max_width"`
	LazyLoading *bool    `yaml:"lazy_loading"`
	Dimensions  *bool    `yaml:"dimensions"`
}

type LanguageConfig

type LanguageConfig struct {
	Name   string `yaml:"name"`
	Title  string `yaml:"title"`
	Weight int    `yaml:"weight"`
	Dir    string `yaml:"dir"` // "ltr" or "rtl"
}

type LastUpdatedStrategy

type LastUpdatedStrategy string

LastUpdatedStrategy is a string enum with back-compat for the legacy bool form.

func (*LastUpdatedStrategy) UnmarshalYAML

func (l *LastUpdatedStrategy) UnmarshalYAML(value *yaml.Node) error

UnmarshalYAML accepts strings ("git", "mtime", "false") and legacy bools (true → "mtime", false → "false") with a deprecation warning.

type LinkValidationSettings

type LinkValidationSettings struct {
	Enabled              *bool                 `yaml:"enabled"`
	Level                string                `yaml:"level"`
	OnBroken             string                `yaml:"on_broken"`              // "error" (default) | "warn" | "ignore"
	OnBrokenAnchor       string                `yaml:"on_broken_anchor"`       // "error" (default) | "warn" | "ignore"
	Report               string                `yaml:"report"`                 // "pretty" (default) | "json" | "github-actions"
	OnRelativeLinks      string                `yaml:"on_relative_links"`      // "warn" (default) | "error" | "ignore"
	OnLocalLinks         string                `yaml:"on_local_links"`         // "warn" (default) | "error" | "ignore"
	OnUnverifiedInternal string                `yaml:"on_unverified_internal"` // "warn" (default) | "error" | "ignore" — extension-less internal links that didn't resolve in-lane
	CheckAnchors         *bool                 `yaml:"check_anchors"`
	CheckImages          *bool                 `yaml:"check_images"`
	SameSitePolicy       string                `yaml:"same_site_policy"`
	SiteRootEscapePrefix string                `yaml:"site_root_escape_prefix"` // prefix (e.g. "site:") routing a link to the site root, bypassing lane logic
	Exclude              []string              `yaml:"exclude"`
	Ignore               []string              `yaml:"ignore"`
	FailBuild            *bool                 `yaml:"fail_build"`
	External             ExternalCheckSettings `yaml:"external"`
}

func (*LinkValidationSettings) EffectiveExternalOnBroken

func (s *LinkValidationSettings) EffectiveExternalOnBroken() string

func (*LinkValidationSettings) EffectiveOnBroken

func (s *LinkValidationSettings) EffectiveOnBroken() string

func (*LinkValidationSettings) EffectiveOnBrokenAnchor

func (s *LinkValidationSettings) EffectiveOnBrokenAnchor() string
func (s *LinkValidationSettings) EffectiveOnLocalLinks() string
func (s *LinkValidationSettings) EffectiveOnRelativeLinks() string

func (*LinkValidationSettings) EffectiveOnUnverifiedInternal

func (s *LinkValidationSettings) EffectiveOnUnverifiedInternal() string

func (*LinkValidationSettings) EffectiveReport

func (s *LinkValidationSettings) EffectiveReport() string

type LlmsTxtSettings

type LlmsTxtSettings struct {
	Enabled     *bool `yaml:"enabled"`
	IncludeBlog *bool `yaml:"include_blog"`
}
type Logo struct {
	Light string `yaml:"light"`
	Dark  string `yaml:"dark"`
	Alt   string `yaml:"alt"`
}

Logo supports both string and object forms in YAML:

logo: "/img/logo.svg"
logo:
  light: "/img/logo-light.svg"
  dark:  "/img/logo-dark.svg"
  alt:   "My Site"

func (*Logo) UnmarshalYAML

func (l *Logo) UnmarshalYAML(value *yaml.Node) error

type MarkdownSettings

type MarkdownSettings struct {
	KaTeX               *bool               `yaml:"katex"`
	Mermaid             *bool               `yaml:"mermaid"`
	CDN                 *bool               `yaml:"cdn"`
	Unsafe              *bool               `yaml:"unsafe"`
	Typographer         *bool               `yaml:"typographer"`
	GithubAlerts        *bool               `yaml:"github_alerts"`
	TripleColonCallouts *bool               `yaml:"triple_colon_callouts"`
	TOC                 MarkdownTOCSettings `yaml:"toc"`
	Codeblocks          CodeblocksSettings  `yaml:"codeblocks"`
}

type MarkdownTOCSettings

type MarkdownTOCSettings struct {
	MinHeadingLevel int `yaml:"min_heading_level"`
	MaxHeadingLevel int `yaml:"max_heading_level"`
}
type NavLink struct {
	Label    string `yaml:"label"`
	URL      string `yaml:"url"`
	External bool   `yaml:"external"`
}

NavLink is a labeled URL used in header and footer navigation.

type PluginSettings

type PluginSettings struct {
	Enabled []string                  `yaml:"enabled"`
	Config  map[string]map[string]any `yaml:"config"`
}

type PrefetchSettings

type PrefetchSettings struct {
	Enabled  *bool  `yaml:"enabled"`
	Strategy string `yaml:"strategy"`
	Delay    int    `yaml:"delay"`
}

type ResolveOptions

type ResolveOptions struct {
	ConfigPath   string         // path to sarde.yaml (default: "sarde.yaml")
	ThemeDir     string         // path to active theme dir (empty = skip theme layer)
	CLIFlags     map[string]any // flag overrides from Cobra
	EnvPrefix    string         // env var prefix (default: "SARDE")
	Strict       bool           // reject unknown fields in user sarde.yaml
	KnownPlugins []string       // valid plugin names (collected by build layer from registries)
}

ResolveOptions provides inputs for the 5-layer config cascade.

type SearchSettings

type SearchSettings struct {
	Enabled  *bool  `yaml:"enabled"`
	Provider string `yaml:"provider"`
}

type SecurityConfig

type SecurityConfig struct {
	BlockedHrefSchemes []string `yaml:"blocked_href_schemes"`
}

SecurityConfig holds security-related settings for content rendering.

type ServerSettings

type ServerSettings struct {
	Host       string `yaml:"host"`
	Port       int    `yaml:"port"`
	LiveReload *bool  `yaml:"live_reload"`
}

type SidebarItem

type SidebarItem struct {
	Label     string        `yaml:"label"`
	Link      string        `yaml:"link,omitempty"`
	Collapsed *bool         `yaml:"collapsed,omitempty"`
	Items     []SidebarItem `yaml:"items,omitempty"`
}

SidebarItem is a single entry in a manually-defined sidebar.

type SidebarSettings

type SidebarSettings struct {
	Collapsed    *bool         `yaml:"collapsed"`
	Badges       *bool         `yaml:"badges"`
	Pagination   *bool         `yaml:"pagination"`
	AutoGenerate *bool         `yaml:"auto_generate"`
	Items        []SidebarItem `yaml:"items"`
}

type SiteConfig

type SiteConfig struct {
	Site           SiteIdentity                     `yaml:"site"`
	Social         []SocialLink                     `yaml:"social"`
	Theme          ThemeSettings                    `yaml:"theme"`
	TOC            TOCSettings                      `yaml:"toc"`
	Sidebar        SidebarSettings                  `yaml:"sidebar"`
	Header         HeaderSettings                   `yaml:"header"`
	Footer         FooterSettings                   `yaml:"footer"`
	Head           HeadSettings                     `yaml:"head"`
	Build          BuildSettings                    `yaml:"build"`
	Markdown       MarkdownSettings                 `yaml:"markdown"`
	Prefetch       PrefetchSettings                 `yaml:"prefetch"`
	Images         ImageSettings                    `yaml:"images"`
	Search         SearchSettings                   `yaml:"search"`
	Icons          IconSettings                     `yaml:"icons"`
	LinkValidation LinkValidationSettings           `yaml:"link_validation"`
	ContentLint    ContentLintSettings              `yaml:"content_lint"`
	Analytics      AnalyticsSettings                `yaml:"analytics"`
	Deploy         DeployConfig                     `yaml:"deploy"`
	Redirects      map[string]string                `yaml:"redirects"`
	Collections    map[string]*CollectionSiteConfig `yaml:"collections"`
	Homepage       HomepageSettings                 `yaml:"homepage"`
	Plugins        PluginSettings                   `yaml:"plugins"`
	Taxonomies     map[string]TaxonomyConfig        `yaml:"taxonomies"`
	Server         ServerSettings                   `yaml:"server"`
	Permalinks     map[string]string                `yaml:"permalinks"`
	I18n           I18nSettings                     `yaml:"i18n"`
	Content        ContentSettings                  `yaml:"content"`
	LlmsTxt        LlmsTxtSettings                  `yaml:"llms_txt"`
	Security       SecurityConfig                   `yaml:"security"`
}

SiteConfig is the complete site configuration. Every field maps to a top-level key in sarde.yaml. Booleans use *bool so the merge layer can distinguish "not set" from "explicitly false".

func Defaults

func Defaults() *SiteConfig

Defaults returns a fully populated SiteConfig from the embedded default YAML. This is layer 1 of the 5-layer config cascade. Panics if the embedded YAML is invalid (programmer error).

func LoadFile

func LoadFile(path string) (*SiteConfig, error)

LoadFile reads a YAML config file and unmarshals it into a SiteConfig. Returns (nil, nil) if the file does not exist — missing config is not an error under the zero-config philosophy. Returns (nil, error) for invalid YAML.

func LoadFileStrict

func LoadFileStrict(path string) (*SiteConfig, error)

LoadFileStrict reads a YAML config file with unknown-field detection enabled. Any field in the YAML that doesn't map to a SiteConfig struct field causes an error. Used only for the user's sarde.yaml (layer 3).

func Resolve

func Resolve(opts ResolveOptions) (*SiteConfig, error)

Resolve loads and merges all config layers, returning the final SiteConfig.

Layer precedence (last wins):

  1. Embedded defaults (compiled into binary)
  2. theme.yaml (from active theme directory)
  3. sarde.yaml (user's project-level config)
  4. CLI flags
  5. Environment variables (SARDE_ prefix)

type SiteIdentity

type SiteIdentity struct {
	Title          string `yaml:"title"`
	Description    string `yaml:"description"`
	URL            string `yaml:"url"`
	Favicon        string `yaml:"favicon"`
	Language       string `yaml:"language"`
	EditURL        string `yaml:"edit_url"`
	TitleDelimiter string `yaml:"title_delimiter"`
	HeadingLinks   *bool  `yaml:"heading_links"`
	Custom404      string `yaml:"custom_404"`
}
type SocialLink struct {
	Label string `yaml:"label"`
	URL   string `yaml:"url"`
	Icon  string `yaml:"icon"`
}

type TOCSettings

type TOCSettings struct {
	Enabled  *bool `yaml:"enabled"`
	MinLevel int   `yaml:"min_level"`
	MaxLevel int   `yaml:"max_level"`
}

type TaxonomyConfig

type TaxonomyConfig struct {
	Singular      string `yaml:"singular"`
	PaginateBy    int    `yaml:"paginate_by"`
	UndefinedTags string `yaml:"undefined_tags"`
	Render        *bool  `yaml:"render"`
}

TaxonomyConfig holds per-taxonomy settings. Accepts both short form ("tag") and full form ({singular: "tag", paginate_by: 20}) in YAML.

func (TaxonomyConfig) ShouldRender

func (tc TaxonomyConfig) ShouldRender() bool

ShouldRender returns true if this taxonomy should generate pages. Default is true when Render is nil (not explicitly set).

func (*TaxonomyConfig) UnmarshalYAML

func (tc *TaxonomyConfig) UnmarshalYAML(value *yaml.Node) error

type ThemeSettings

type ThemeSettings struct {
	Name          string            `yaml:"name"`
	Preset        string            `yaml:"preset"`
	Dark          *bool             `yaml:"dark"`
	Overrides     map[string]string `yaml:"overrides"`
	DarkOverrides map[string]string `yaml:"dark_overrides"`
	PrimaryColor  string            `yaml:"primary_color"`
	AccentColor   string            `yaml:"accent_color"`
	FontFamily    string            `yaml:"font_family"`
	FontMono      string            `yaml:"font_mono"`
	CodeLight     string            `yaml:"code_light"`
	CodeDark      string            `yaml:"code_dark"`
}

type VersionBanner

type VersionBanner string
const (
	BannerNone         VersionBanner = "none"
	BannerUnmaintained VersionBanner = "unmaintained"
	BannerUnreleased   VersionBanner = "unreleased"
)

type VersionEntry

type VersionEntry struct {
	ID       string          `yaml:"id"`
	Label    string          `yaml:"label"`
	Path     string          `yaml:"path"`
	Banner   VersionBanner   `yaml:"banner"`
	Redirect VersionRedirect `yaml:"redirect"`
}

VersionEntry describes one version of a versioned docs collection.

type VersionRedirect

type VersionRedirect string
const (
	RedirectSamePage VersionRedirect = "same-page"
	RedirectRoot     VersionRedirect = "root"
)

type VersioningConfig

type VersioningConfig struct {
	Enabled                   *bool          `yaml:"enabled"`
	LastVersion               string         `yaml:"last_version"`
	PublishLatestAtVersionURL bool           `yaml:"publish_latest_at_version_url"`
	Fallback                  string         `yaml:"fallback"` // "" (inherit site), "default", or "omit"
	Versions                  []VersionEntry `yaml:"versions"`
}

VersioningConfig controls docs versioning for a collection.

Jump to

Keyboard shortcuts

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