3 Topics-and-Catalog
Virgil edited this page 2026-02-19 16:58:36 +00:00

Topics and Catalogue

Help content is organised as Topic values managed by a Catalog. Topics can be created programmatically, loaded from Markdown files with YAML frontmatter, or provided by the built-in defaults.

Topic Structure

type Topic struct {
    ID       string    // URL-safe slug
    Title    string    // Human-readable title
    Path     string    // Source file path (if parsed from file)
    Content  string    // Markdown body
    Sections []Section // Parsed headings
    Tags     []string  // Searchable tags
    Related  []string  // Related topic IDs
    Order    int       // Sort priority
}

YAML Frontmatter

Markdown files can include YAML frontmatter delimited by ---:

---
title: Deployment Guide
tags:
  - ops
  - docker
  - production
related:
  - configuration
  - monitoring
order: 10
---

# Deployment Guide

How to deploy the application to production...

The frontmatter is parsed into a Frontmatter struct:

type Frontmatter struct {
    Title   string   `yaml:"title"`
    Tags    []string `yaml:"tags"`
    Related []string `yaml:"related"`
    Order   int      `yaml:"order"`
}

If title is provided in the frontmatter, it takes precedence. Otherwise the parser falls back to the first # heading in the document.

Parsing Markdown Files

ParseTopic processes a Markdown file into a structured topic:

content, _ := os.ReadFile("docs/deployment.md")
topic, err := help.ParseTopic("docs/deployment.md", content)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("ID: %s\n", topic.ID)           // "deployment-guide"
fmt.Printf("Title: %s\n", topic.Title)     // "Deployment Guide"
fmt.Printf("Sections: %d\n", len(topic.Sections))
fmt.Printf("Tags: %v\n", topic.Tags)       // ["ops", "docker", "production"]

The parsing pipeline:

  1. Extract YAML frontmatter (if present)
  2. Generate an ID from the title using GenerateID
  3. Parse headings into Section values with ExtractSections
  4. Fall back to the first H1 heading if no frontmatter title

Section Extraction

ExtractSections parses Markdown headings (# through ######) into structured sections:

sections := help.ExtractSections(markdownBody)
for _, s := range sections {
    fmt.Printf("L%d [line %d] %s: %s...\n",
        s.Level, s.Line, s.Title, s.Content[:50])
}

Each section captures:

  • ID: URL-safe slug of the heading text
  • Title: The heading text
  • Level: 1-6 corresponding to # through ######
  • Line: 1-indexed line number in the content
  • Content: All text between this heading and the next

ID Generation

GenerateID converts titles to URL-safe slugs:

help.GenerateID("Getting Started")  // "getting-started"
help.GenerateID("API Reference")    // "api-reference"
help.GenerateID("FAQ & Tips!")       // "faq-tips"

Rules: lowercase, letters/digits kept, spaces/hyphens/underscores become hyphens, other characters dropped, trailing hyphens trimmed.

Catalogue Management

The Catalog holds topics in a map and maintains a search index:

cat := help.DefaultCatalog()

// Add topics
cat.Add(&help.Topic{
    ID:      "troubleshooting",
    Title:   "Troubleshooting",
    Content: "# Troubleshooting\n\nCommon issues and solutions...",
    Tags:    []string{"debug", "errors"},
})

// Retrieve by ID
topic, err := cat.Get("getting-started")
if err != nil {
    fmt.Println("not found")
}

// List all
for _, t := range cat.List() {
    fmt.Printf("  %s\n", t.Title)
}

Default Topics

DefaultCatalog() includes two built-in topics:

  • getting-started -- Overview of common commands and next steps
  • config -- Environment variables and config file locations

These serve as a baseline and can be supplemented with Add.

See also: Home | Search-Engine