From 5fa5246e9f0c1436f3e74f9946e59111f710bc1a Mon Sep 17 00:00:00 2001 From: vickydotbat Date: Sun, 24 May 2026 18:06:28 +0200 Subject: [PATCH] Alignment links --- README.md | 12 ++++++++++++ internal/project/effective.go | 1 + internal/project/project.go | 5 +++++ internal/topdata/wiki_native.go | 2 ++ internal/topdata/wiki_native_test.go | 22 ++++++++++++++++++++++ internal/topdata/wiki_tables.go | 2 +- internal/topdata/wiki_template_expr.go | 26 +++++++++++++++++++++++--- 7 files changed, 66 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 59a0f9b..eb67df1 100644 --- a/README.md +++ b/README.md @@ -283,6 +283,18 @@ topdata: value: 0 ``` +`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: + +```yaml +topdata: + wiki: + alignment_links: + target_pattern: Guides/Alignment/{alignment} +``` + `paths.source` and `paths.assets` must name real repository subtrees when they are configured. They may be omitted for repositories that do not own that class of source, but they must not resolve to the repository root (`.`). Output and diff --git a/internal/project/effective.go b/internal/project/effective.go index 96c0333..456d008 100644 --- a/internal/project/effective.go +++ b/internal/project/effective.go @@ -256,6 +256,7 @@ func (p *Project) EffectiveConfig() EffectiveConfig { VisibilityFile: defaultString(p.Config.TopData.Wiki.VisibilityFile, DefaultTopDataWikiVisibilityFile), TemplatesDir: defaultString(p.Config.TopData.Wiki.TemplatesDir, DefaultTopDataWikiTemplatesDir), ManualSectionsDir: defaultString(p.Config.TopData.Wiki.ManualSectionsDir, DefaultTopDataWikiManualSectionsDir), + AlignmentLinks: p.Config.TopData.Wiki.AlignmentLinks, TitlePrefixMinLength: defaultInt(p.Config.TopData.Wiki.TitlePrefixMinLength, DefaultTopDataWikiTitlePrefixMinLength), StatusPages: defaultBoolPointer(p.Config.TopData.Wiki.StatusPages, DefaultTopDataWikiStatusPages), StatusListingScope: defaultString(p.Config.TopData.Wiki.StatusListingScope, DefaultTopDataWikiStatusListingScope), diff --git a/internal/project/project.go b/internal/project/project.go index c588e45..73ab07c 100644 --- a/internal/project/project.go +++ b/internal/project/project.go @@ -249,6 +249,7 @@ type TopDataWikiConfig struct { VisibilityFile string `json:"visibility_file" yaml:"visibility_file"` TemplatesDir string `json:"templates_dir" yaml:"templates_dir"` ManualSectionsDir string `json:"manual_sections_dir" yaml:"manual_sections_dir"` + AlignmentLinks TopDataWikiAlignmentLinks `json:"alignment_links" yaml:"alignment_links"` TitlePrefixMinLength int `json:"title_prefix_min_length" yaml:"title_prefix_min_length"` StatusPages *bool `json:"status_pages,omitempty" yaml:"status_pages,omitempty"` StatusListingScope string `json:"status_listing_scope" yaml:"status_listing_scope"` @@ -256,6 +257,10 @@ type TopDataWikiConfig struct { ManagedRegion TopDataWikiManagedRegionConfig `json:"managed_region" yaml:"managed_region"` } +type TopDataWikiAlignmentLinks struct { + TargetPattern string `json:"target_pattern" yaml:"target_pattern"` +} + type TopDataWikiStalePagesConfig struct { Default string `json:"default" yaml:"default"` LiveCleanup string `json:"live_cleanup" yaml:"live_cleanup"` diff --git a/internal/topdata/wiki_native.go b/internal/topdata/wiki_native.go index c239455..a00a9dc 100644 --- a/internal/topdata/wiki_native.go +++ b/internal/topdata/wiki_native.go @@ -120,6 +120,7 @@ type wikiContext struct { visibleWikiKeys map[string]bool namespaceTitles map[string]string namespacePaths map[string]string + alignmentLinkPattern string } func buildWiki(p *project.Project, nativeResult BuildResult, force bool, progress func(string)) (wikiResult, error) { @@ -153,6 +154,7 @@ func buildWiki(p *project.Project, nativeResult BuildResult, force bool, progres manualSections := loadWikiManualSections(p) ctx.manualSections = manualSections ctx.templateDir = filepath.Join(p.TopDataWikiSourceDir(), filepath.FromSlash(p.EffectiveConfig().TopData.Wiki.TemplatesDir), "pages") + ctx.alignmentLinkPattern = p.EffectiveConfig().TopData.Wiki.AlignmentLinks.TargetPattern ctx.wikiTablesPath = filepath.Join(p.TopDataWikiSourceDir(), filepath.FromSlash(p.EffectiveConfig().TopData.Wiki.TablesFile)) tableDefinitions, err := loadWikiTableDefinitions(ctx.wikiTablesPath) if err != nil { diff --git a/internal/topdata/wiki_native_test.go b/internal/topdata/wiki_native_test.go index 7201c91..23524a0 100644 --- a/internal/topdata/wiki_native_test.go +++ b/internal/topdata/wiki_native_test.go @@ -1288,6 +1288,28 @@ func TestWikiTemplateFormatsPreferredAlignments(t *testing.T) { } } +func TestWikiTemplateFormatsPreferredAlignmentsWithConfiguredLinks(t *testing.T) { + ctx := &wikiContext{alignmentLinkPattern: "Guides/Alignment/{alignment}"} + page := wikiTemplatePage{ + PageID: "racialtypes:tiefling", + Category: "racialtypes", + Key: "racialtypes:tiefling", + Title: "Tiefling", + Row: map[string]any{ + "PreferredAlignments": map[string]any{"list": []any{"le", "ne", "ce"}}, + }, + } + + got, err := ctx.renderWikiTemplateString("racialtypes.html", `{{alignments(PreferredAlignments)}}`, page) + if err != nil { + t.Fatalf("render template: %v", err) + } + want := "[[Guides/Alignment/Lawful_Evil|Lawful Evil]], [[Guides/Alignment/Neutral_Evil|Neutral Evil]], [[Guides/Alignment/Chaotic_Evil|Chaotic Evil]]" + if got != want { + t.Fatalf("expected %q, got %q", want, got) + } +} + func TestWikiTemplateDescriptionFiltersExtractParagraphsAndSections(t *testing.T) { ctx := &wikiContext{} page := wikiTemplatePage{ diff --git a/internal/topdata/wiki_tables.go b/internal/topdata/wiki_tables.go index d62e98a..caa9611 100644 --- a/internal/topdata/wiki_tables.go +++ b/internal/topdata/wiki_tables.go @@ -1059,7 +1059,7 @@ func (p *wikiExprParser) callHelper(name string, args []any) (any, error) { if len(args) != 1 { return nil, fmt.Errorf("alignments expects 1 argument") } - return formatWikiTemplateAlignments(args[0]) + return p.ctx.formatWikiTemplateAlignments(args[0]) case "ability_adjustments": if len(args) != 0 { return nil, fmt.Errorf("ability_adjustments expects 0 arguments") diff --git a/internal/topdata/wiki_template_expr.go b/internal/topdata/wiki_template_expr.go index 982449e..3e05f1e 100644 --- a/internal/topdata/wiki_template_expr.go +++ b/internal/topdata/wiki_template_expr.go @@ -188,7 +188,7 @@ func (ctx *wikiContext) applyWikiTemplateFilter(value any, filter string, state case "count": return countWikiTemplateValue(value), nil case "alignments": - return formatWikiTemplateAlignments(value) + return ctx.formatWikiTemplateAlignments(value) case "list": return wikiTemplateHTML(renderWikiTemplateList(value, firstArg(args))), nil case "ordinal": @@ -367,7 +367,7 @@ func joinWikiTemplateValue(value any, sep string) string { } } -func formatWikiTemplateAlignments(value any) (string, error) { +func (ctx *wikiContext) formatWikiTemplateAlignments(value any) (string, error) { values, err := wikiTemplateAlignmentValues(value) if err != nil { return "", err @@ -384,7 +384,7 @@ func formatWikiTemplateAlignments(value any) (string, error) { if !ok { return "", fmt.Errorf("unknown alignment value 0x%02X", value) } - labels = append(labels, label) + labels = append(labels, ctx.renderWikiTemplateAlignmentLabel(label)) } if len(labels) == 0 { return "Any", nil @@ -392,6 +392,26 @@ func formatWikiTemplateAlignments(value any) (string, error) { return strings.Join(labels, ", "), nil } +func (ctx *wikiContext) renderWikiTemplateAlignmentLabel(label string) string { + pattern := "" + if ctx != nil { + pattern = strings.TrimSpace(ctx.alignmentLinkPattern) + } + if pattern == "" { + return label + } + target := strings.ReplaceAll(pattern, "{alignment}", wikiTemplateAlignmentTargetSegment(label)) + target = strings.ReplaceAll(target, "{label}", label) + if strings.TrimSpace(target) == "" { + return label + } + return "[[" + target + "|" + label + "]]" +} + +func wikiTemplateAlignmentTargetSegment(label string) string { + return strings.ReplaceAll(strings.TrimSpace(label), " ", "_") +} + func wikiTemplateAlignmentValues(value any) ([]int, error) { if isNullLike(value) { return []int{0}, nil