Topdata wiki alignment links

This commit is contained in:
2026-05-25 10:31:07 +02:00
parent 7714c068fe
commit a646c905e4
7 changed files with 126 additions and 68 deletions
+6 -2
View File
@@ -326,13 +326,17 @@ the toolkit keeps its legacy default injections for compatibility.
`topdata.wiki.alignment_links.target_pattern` controls whether formatted
alignment names become internal wiki links. The pattern may use `{alignment}`
for the title-shaped alignment segment with spaces converted to underscores, and
`{label}` for the display label:
`{label}` for the display label. `alignment_links.link_format` defaults to
`wiki_markup`, which emits `[[target|label]]`. Use `html_anchor` when the
configured target should be emitted as a root URL anchor instead of being
resolved as namespace-relative wiki markup:
```yaml
topdata:
wiki:
alignment_links:
target_pattern: Guides/Alignment/{alignment}
target_pattern: /wiki/Guides/Alignment/{alignment}
link_format: html_anchor
```
Generated wiki input and output paths are YAML-configurable. `source` points to
+6 -1
View File
@@ -55,6 +55,7 @@ const (
DefaultTopDataWikiTitlePrefixMinLength = 3
DefaultTopDataWikiStatusPages = true
DefaultTopDataWikiStatusListingScope = "all"
DefaultTopDataWikiAlignmentLinkFormat = "wiki_markup"
DefaultWikiDeployManifest = ".wiki_deploy_manifest.json"
DefaultWikiDeployEditSummary = "Auto-generated from native builder"
DefaultTopDataPackageHAK = "sow_top.hak"
@@ -270,7 +271,10 @@ func (p *Project) EffectiveConfig() EffectiveConfig {
ManualSectionsDir: defaultString(p.Config.TopData.Wiki.ManualSectionsDir, DefaultTopDataWikiManualSectionsDir),
ManualSectionsFile: defaultWikiManualSectionsFile(p.Config.TopData.Wiki),
PageIndexFile: defaultString(p.Config.TopData.Wiki.PageIndexFile, DefaultTopDataWikiPageIndexFile),
AlignmentLinks: p.Config.TopData.Wiki.AlignmentLinks,
AlignmentLinks: TopDataWikiAlignmentLinks{
TargetPattern: strings.TrimSpace(p.Config.TopData.Wiki.AlignmentLinks.TargetPattern),
LinkFormat: defaultString(p.Config.TopData.Wiki.AlignmentLinks.LinkFormat, DefaultTopDataWikiAlignmentLinkFormat),
},
TitlePrefixMinLength: defaultInt(p.Config.TopData.Wiki.TitlePrefixMinLength, DefaultTopDataWikiTitlePrefixMinLength),
StatusPages: defaultBoolPointer(p.Config.TopData.Wiki.StatusPages, DefaultTopDataWikiStatusPages),
StatusListingScope: defaultString(p.Config.TopData.Wiki.StatusListingScope, DefaultTopDataWikiStatusListingScope),
@@ -571,6 +575,7 @@ func markMissingDefaults(provenance ConfigProvenance) {
"topdata.wiki.title_prefix_min_length": "3",
"topdata.wiki.status_pages": "true",
"topdata.wiki.status_listing_scope": DefaultTopDataWikiStatusListingScope,
"topdata.wiki.alignment_links.link_format": DefaultTopDataWikiAlignmentLinkFormat,
"topdata.wiki.stale_pages.default": DefaultTopDataWikiStaleDefault,
"topdata.wiki.stale_pages.live_cleanup": DefaultTopDataWikiStaleLiveCleanup,
"topdata.wiki.managed_region.marker_format": DefaultTopDataWikiMarkerFormat,
+7
View File
@@ -294,6 +294,7 @@ type TopDataWikiConfig struct {
type TopDataWikiAlignmentLinks struct {
TargetPattern string `json:"target_pattern" yaml:"target_pattern"`
LinkFormat string `json:"link_format" yaml:"link_format"`
}
type TopDataWikiStalePagesConfig struct {
@@ -603,6 +604,7 @@ func (p *Project) ValidateLayout() error {
"topdata.wiki.manual_sections_dir": p.Config.TopData.Wiki.ManualSectionsDir,
"topdata.wiki.manual_sections_file": p.Config.TopData.Wiki.ManualSectionsFile,
"topdata.wiki.page_index_file": p.Config.TopData.Wiki.PageIndexFile,
"topdata.wiki.alignment_links.link_format": p.Config.TopData.Wiki.AlignmentLinks.LinkFormat,
"extract.layout": p.Config.Extract.Layout,
"extract.hak_discovery": p.Config.Extract.HAKDiscovery,
"autogen.cache.root": p.Config.Autogen.Cache.Root,
@@ -674,6 +676,11 @@ func (p *Project) ValidateLayout() error {
default:
failures = append(failures, fmt.Errorf("topdata.wiki.link_strategy %q is not supported", effective.TopData.Wiki.LinkStrategy))
}
switch effective.TopData.Wiki.AlignmentLinks.LinkFormat {
case "wiki_markup", "html_anchor":
default:
failures = append(failures, fmt.Errorf("topdata.wiki.alignment_links.link_format %q is not supported", effective.TopData.Wiki.AlignmentLinks.LinkFormat))
}
if effective.TopData.Wiki.TitlePrefixMinLength < 1 {
failures = append(failures, fmt.Errorf("topdata.wiki.title_prefix_min_length must be at least 1"))
}
+4
View File
@@ -1206,6 +1206,9 @@ func TestValidateLayoutRejectsInvalidTopDataWikiConfig(t *testing.T) {
ManualSectionsFile: "../manual/default.yaml",
PageIndexFile: "../page-index.json",
ManualSectionsDir: "../manual",
AlignmentLinks: TopDataWikiAlignmentLinks{
LinkFormat: "raw_url",
},
StalePages: TopDataWikiStalePagesConfig{
Default: "delete",
LiveCleanup: "delete",
@@ -1234,6 +1237,7 @@ func TestValidateLayoutRejectsInvalidTopDataWikiConfig(t *testing.T) {
"topdata.wiki.manual_sections_file",
"topdata.wiki.page_index_file",
"topdata.wiki.manual_sections_dir",
"topdata.wiki.alignment_links.link_format",
"topdata.wiki.stale_pages.default",
"topdata.wiki.stale_pages.live_cleanup",
} {
+2
View File
@@ -121,6 +121,7 @@ type wikiContext struct {
namespaceTitles map[string]string
namespacePaths map[string]string
alignmentLinkPattern string
alignmentLinkFormat string
}
func buildWiki(p *project.Project, nativeResult BuildResult, force bool, progress func(string)) (wikiResult, error) {
@@ -155,6 +156,7 @@ func buildWiki(p *project.Project, nativeResult BuildResult, force bool, progres
ctx.manualSections = manualSections
ctx.templateDir = p.TopDataWikiPageTemplatesDir()
ctx.alignmentLinkPattern = p.EffectiveConfig().TopData.Wiki.AlignmentLinks.TargetPattern
ctx.alignmentLinkFormat = p.EffectiveConfig().TopData.Wiki.AlignmentLinks.LinkFormat
ctx.wikiTablesPath = filepath.Join(p.TopDataWikiSourceDir(), filepath.FromSlash(p.EffectiveConfig().TopData.Wiki.TablesFile))
tableDefinitions, err := loadWikiTableDefinitions(ctx.wikiTablesPath)
if err != nil {
+25
View File
@@ -1312,6 +1312,31 @@ func TestWikiTemplateFormatsPreferredAlignmentsWithConfiguredLinks(t *testing.T)
}
}
func TestWikiTemplateFormatsPreferredAlignmentsWithConfiguredRootAnchors(t *testing.T) {
ctx := &wikiContext{
alignmentLinkPattern: "/wiki/Guides/Alignment/{alignment}",
alignmentLinkFormat: "html_anchor",
}
page := wikiTemplatePage{
PageID: "racialtypes:tiefling",
Category: "racialtypes",
Key: "racialtypes:tiefling",
Title: "Tiefling",
Row: map[string]any{
"PreferredAlignments": map[string]any{"list": []any{"le"}},
},
}
got, err := ctx.renderWikiTemplateString("racialtypes.html", `{{alignments(PreferredAlignments)}}`, page)
if err != nil {
t.Fatalf("render template: %v", err)
}
want := `<a class="wiki-internal-link" href="/wiki/Guides/Alignment/Lawful_Evil">Lawful Evil</a>`
if got != want {
t.Fatalf("expected %q, got %q", want, got)
}
}
func TestWikiTemplateDescriptionFiltersExtractParagraphsAndSections(t *testing.T) {
ctx := &wikiContext{}
page := wikiTemplatePage{
+13 -2
View File
@@ -367,7 +367,7 @@ func joinWikiTemplateValue(value any, sep string) string {
}
}
func (ctx *wikiContext) formatWikiTemplateAlignments(value any) (string, error) {
func (ctx *wikiContext) formatWikiTemplateAlignments(value any) (any, error) {
values, err := wikiTemplateAlignmentValues(value)
if err != nil {
return "", err
@@ -389,13 +389,21 @@ func (ctx *wikiContext) formatWikiTemplateAlignments(value any) (string, error)
if len(labels) == 0 {
return "Any", nil
}
return strings.Join(labels, ", "), nil
rendered := strings.Join(labels, ", ")
if ctx != nil && strings.TrimSpace(ctx.alignmentLinkFormat) == "html_anchor" {
return wikiTemplateHTML(rendered), nil
}
return rendered, nil
}
func (ctx *wikiContext) renderWikiTemplateAlignmentLabel(label string) string {
pattern := ""
format := "wiki_markup"
if ctx != nil {
pattern = strings.TrimSpace(ctx.alignmentLinkPattern)
if strings.TrimSpace(ctx.alignmentLinkFormat) != "" {
format = strings.TrimSpace(ctx.alignmentLinkFormat)
}
}
if pattern == "" {
return label
@@ -405,6 +413,9 @@ func (ctx *wikiContext) renderWikiTemplateAlignmentLabel(label string) string {
if strings.TrimSpace(target) == "" {
return label
}
if format == "html_anchor" {
return `<a class="wiki-internal-link" href="` + html.EscapeString(target) + `">` + html.EscapeString(label) + `</a>`
}
return "[[" + target + "|" + label + "]]"
}