Some checks failed
Build and Release / Create Release (push) Has been skipped
Build and Release / Unit Tests (push) Failing after 47s
Build and Release / Integration Tests (PostgreSQL) (push) Failing after 1m29s
Build and Release / Lint (push) Failing after 2m16s
Build and Release / Build Binaries (amd64, linux, linux-latest) (push) Has been skipped
Build and Release / Build Binaries (amd64, windows, windows-latest) (push) Has been skipped
Build and Release / Build Binaries (amd64, darwin, macos) (push) Has been skipped
Build and Release / Build Binaries (arm64, darwin, macos) (push) Has been skipped
Build and Release / Build Binary (linux/arm64) (push) Has been skipped
Allow repository pages to serve files directly at specific URL paths instead of rendering the landing page. Supports exact paths (/badge.svg) and glob patterns (/schema/*). Add advanced settings UI and API endpoints for managing static routes alongside existing redirects and custom code options.
535 lines
21 KiB
Go
535 lines
21 KiB
Go
// Copyright 2026 MarketAlly. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package pages
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"slices"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// LandingConfig represents the parsed .gitea/landing.yaml configuration
|
|
type LandingConfig struct {
|
|
Enabled bool `yaml:"enabled" json:"enabled"`
|
|
PublicLanding bool `yaml:"public_landing" json:"public_landing"`
|
|
Template string `yaml:"template" json:"template"` // open-source-hero, minimalist-docs, saas-conversion, bold-marketing
|
|
|
|
// Custom domain (optional)
|
|
Domain string `yaml:"domain,omitempty" json:"domain,omitempty"`
|
|
|
|
// Brand configuration
|
|
Brand BrandConfig `yaml:"brand,omitempty" json:"brand,omitzero"`
|
|
|
|
// Hero section
|
|
Hero HeroConfig `yaml:"hero,omitempty" json:"hero,omitzero"`
|
|
|
|
// Stats/metrics
|
|
Stats []StatConfig `yaml:"stats,omitempty" json:"stats,omitempty"`
|
|
|
|
// Value propositions
|
|
ValueProps []ValuePropConfig `yaml:"value_props,omitempty" json:"value_props,omitempty"`
|
|
|
|
// Features
|
|
Features []FeatureConfig `yaml:"features,omitempty" json:"features,omitempty"`
|
|
|
|
// Social proof
|
|
SocialProof SocialProofConfig `yaml:"social_proof,omitempty" json:"social_proof,omitzero"`
|
|
|
|
// Pricing (for saas-conversion template)
|
|
Pricing PricingConfig `yaml:"pricing,omitempty" json:"pricing,omitzero"`
|
|
|
|
// CTA section
|
|
CTASection CTASectionConfig `yaml:"cta_section,omitempty" json:"cta_section,omitzero"`
|
|
|
|
// Blog section
|
|
Blog BlogSectionConfig `yaml:"blog,omitempty" json:"blog,omitzero"`
|
|
|
|
// Gallery section
|
|
Gallery GallerySectionConfig `yaml:"gallery,omitempty" json:"gallery,omitzero"`
|
|
|
|
// Comparison section
|
|
Comparison ComparisonSectionConfig `yaml:"comparison,omitempty" json:"comparison,omitzero"`
|
|
|
|
// Navigation visibility
|
|
Navigation NavigationConfig `yaml:"navigation,omitempty" json:"navigation,omitzero"`
|
|
|
|
// Footer
|
|
Footer FooterConfig `yaml:"footer,omitempty" json:"footer,omitzero"`
|
|
|
|
// Theme customization
|
|
Theme ThemeConfig `yaml:"theme,omitempty" json:"theme,omitzero"`
|
|
|
|
// SEO & Social
|
|
SEO SEOConfig `yaml:"seo,omitempty" json:"seo,omitzero"`
|
|
|
|
// Analytics
|
|
Analytics AnalyticsConfig `yaml:"analytics,omitempty" json:"analytics,omitzero"`
|
|
|
|
// Advanced settings
|
|
Advanced AdvancedConfig `yaml:"advanced,omitempty" json:"advanced,omitzero"`
|
|
|
|
// A/B testing experiments
|
|
Experiments ExperimentConfig `yaml:"experiments,omitempty" json:"experiments,omitzero"`
|
|
|
|
// Multi-language support
|
|
I18n I18nConfig `yaml:"i18n,omitempty" json:"i18n,omitzero"`
|
|
}
|
|
|
|
// BrandConfig represents brand/identity settings
|
|
type BrandConfig struct {
|
|
Name string `yaml:"name,omitempty" json:"name,omitempty"`
|
|
LogoURL string `yaml:"logo_url,omitempty" json:"logo_url,omitempty"`
|
|
UploadedLogo string `yaml:"uploaded_logo,omitempty" json:"uploaded_logo,omitempty"`
|
|
LogoSource string `yaml:"logo_source,omitempty" json:"logo_source,omitempty"` // "url" (default), "repo", or "org" — selects avatar source
|
|
Tagline string `yaml:"tagline,omitempty" json:"tagline,omitempty"`
|
|
FaviconURL string `yaml:"favicon_url,omitempty" json:"favicon_url,omitempty"`
|
|
UploadedFavicon string `yaml:"uploaded_favicon,omitempty" json:"uploaded_favicon,omitempty"`
|
|
}
|
|
|
|
// ResolvedLogoURL returns the uploaded logo path or the external URL.
|
|
func (b *BrandConfig) ResolvedLogoURL() string {
|
|
if b.UploadedLogo != "" {
|
|
return "/repo-avatars/" + b.UploadedLogo
|
|
}
|
|
return b.LogoURL
|
|
}
|
|
|
|
// ResolvedFaviconURL returns the uploaded favicon path or the external URL.
|
|
func (b *BrandConfig) ResolvedFaviconURL() string {
|
|
if b.UploadedFavicon != "" {
|
|
return "/repo-avatars/" + b.UploadedFavicon
|
|
}
|
|
return b.FaviconURL
|
|
}
|
|
|
|
// HeroConfig represents hero section settings
|
|
type HeroConfig struct {
|
|
Headline string `yaml:"headline,omitempty" json:"headline,omitempty"`
|
|
Subheadline string `yaml:"subheadline,omitempty" json:"subheadline,omitempty"`
|
|
PrimaryCTA CTAButton `yaml:"primary_cta,omitempty" json:"primary_cta,omitzero"`
|
|
SecondaryCTA CTAButton `yaml:"secondary_cta,omitempty" json:"secondary_cta,omitzero"`
|
|
ImageURL string `yaml:"image_url,omitempty" json:"image_url,omitempty"`
|
|
UploadedImage string `yaml:"uploaded_image,omitempty" json:"uploaded_image,omitempty"` // filename in repo-avatars storage
|
|
CodeExample string `yaml:"code_example,omitempty" json:"code_example,omitempty"`
|
|
VideoURL string `yaml:"video_url,omitempty" json:"video_url,omitempty"`
|
|
}
|
|
|
|
// ResolvedImageURL returns the effective hero image URL, preferring uploaded image over URL.
|
|
func (h *HeroConfig) ResolvedImageURL() string {
|
|
if h.UploadedImage != "" {
|
|
return "/repo-avatars/" + h.UploadedImage
|
|
}
|
|
return h.ImageURL
|
|
}
|
|
|
|
// CTAButton represents a call-to-action button
|
|
type CTAButton struct {
|
|
Label string `yaml:"label,omitempty" json:"label,omitempty"`
|
|
URL string `yaml:"url,omitempty" json:"url,omitempty"`
|
|
Variant string `yaml:"variant,omitempty" json:"variant,omitempty"` // primary, secondary, outline, text
|
|
}
|
|
|
|
// StatConfig represents a single stat/metric
|
|
type StatConfig struct {
|
|
Value string `yaml:"value,omitempty" json:"value,omitempty"`
|
|
Label string `yaml:"label,omitempty" json:"label,omitempty"`
|
|
}
|
|
|
|
// ValuePropConfig represents a value proposition
|
|
type ValuePropConfig struct {
|
|
Title string `yaml:"title,omitempty" json:"title,omitempty"`
|
|
Description string `yaml:"description,omitempty" json:"description,omitempty"`
|
|
Icon string `yaml:"icon,omitempty" json:"icon,omitempty"`
|
|
}
|
|
|
|
// FeatureConfig represents a single feature item
|
|
type FeatureConfig struct {
|
|
Title string `yaml:"title,omitempty" json:"title,omitempty"`
|
|
Description string `yaml:"description,omitempty" json:"description,omitempty"`
|
|
Icon string `yaml:"icon,omitempty" json:"icon,omitempty"`
|
|
ImageURL string `yaml:"image_url,omitempty" json:"image_url,omitempty"`
|
|
}
|
|
|
|
// SocialProofConfig represents social proof section
|
|
type SocialProofConfig struct {
|
|
Logos []string `yaml:"logos,omitempty" json:"logos,omitempty"`
|
|
Testimonial TestimonialConfig `yaml:"testimonial,omitempty" json:"testimonial,omitzero"`
|
|
Testimonials []TestimonialConfig `yaml:"testimonials,omitempty" json:"testimonials,omitempty"`
|
|
}
|
|
|
|
// TestimonialConfig represents a testimonial
|
|
type TestimonialConfig struct {
|
|
Quote string `yaml:"quote,omitempty" json:"quote,omitempty"`
|
|
Author string `yaml:"author,omitempty" json:"author,omitempty"`
|
|
Role string `yaml:"role,omitempty" json:"role,omitempty"`
|
|
Avatar string `yaml:"avatar,omitempty" json:"avatar,omitempty"`
|
|
}
|
|
|
|
// PricingConfig represents pricing section
|
|
type PricingConfig struct {
|
|
Headline string `yaml:"headline,omitempty" json:"headline,omitempty"`
|
|
Subheadline string `yaml:"subheadline,omitempty" json:"subheadline,omitempty"`
|
|
Plans []PricingPlanConfig `yaml:"plans,omitempty" json:"plans,omitempty"`
|
|
}
|
|
|
|
// PricingPlanConfig represents a pricing plan
|
|
type PricingPlanConfig struct {
|
|
Name string `yaml:"name,omitempty" json:"name,omitempty"`
|
|
Price string `yaml:"price,omitempty" json:"price,omitempty"`
|
|
Period string `yaml:"period,omitempty" json:"period,omitempty"`
|
|
Features []string `yaml:"features,omitempty" json:"features,omitempty"`
|
|
CTA string `yaml:"cta,omitempty" json:"cta,omitempty"`
|
|
Featured bool `yaml:"featured,omitempty" json:"featured,omitempty"`
|
|
}
|
|
|
|
// CTASectionConfig represents the final CTA section
|
|
type CTASectionConfig struct {
|
|
Headline string `yaml:"headline,omitempty" json:"headline,omitempty"`
|
|
Subheadline string `yaml:"subheadline,omitempty" json:"subheadline,omitempty"`
|
|
Button CTAButton `yaml:"button,omitempty" json:"button,omitzero"`
|
|
}
|
|
|
|
// BlogSectionConfig represents blog section settings on the landing page
|
|
type BlogSectionConfig struct {
|
|
Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
|
|
Headline string `yaml:"headline,omitempty" json:"headline,omitempty"`
|
|
Subheadline string `yaml:"subheadline,omitempty" json:"subheadline,omitempty"`
|
|
MaxPosts int `yaml:"max_posts,omitempty" json:"max_posts,omitempty"` // default 3
|
|
ShowExcerpt bool `yaml:"show_excerpt,omitempty" json:"show_excerpt,omitempty"` // show subtitle as excerpt
|
|
CTAButton CTAButton `yaml:"cta_button,omitempty" json:"cta_button,omitzero"` // "View All Posts" link
|
|
}
|
|
|
|
// GallerySectionConfig represents gallery section settings on the landing page
|
|
type GallerySectionConfig struct {
|
|
Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
|
|
Headline string `yaml:"headline,omitempty" json:"headline,omitempty"`
|
|
Subheadline string `yaml:"subheadline,omitempty" json:"subheadline,omitempty"`
|
|
MaxImages int `yaml:"max_images,omitempty" json:"max_images,omitempty"` // default 6
|
|
Columns int `yaml:"columns,omitempty" json:"columns,omitempty"` // grid columns, default 3
|
|
}
|
|
|
|
// ComparisonSectionConfig represents a feature comparison matrix section
|
|
type ComparisonSectionConfig struct {
|
|
Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
|
|
Headline string `yaml:"headline,omitempty" json:"headline,omitempty"`
|
|
Subheadline string `yaml:"subheadline,omitempty" json:"subheadline,omitempty"`
|
|
Columns []ComparisonColumnConfig `yaml:"columns,omitempty" json:"columns,omitempty"`
|
|
Groups []ComparisonGroupConfig `yaml:"groups,omitempty" json:"groups,omitempty"`
|
|
}
|
|
|
|
// HasData returns true if the comparison section has columns and at least one feature
|
|
func (c *ComparisonSectionConfig) HasData() bool {
|
|
if len(c.Columns) == 0 || len(c.Groups) == 0 {
|
|
return false
|
|
}
|
|
for _, g := range c.Groups {
|
|
if len(g.Features) > 0 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// ComparisonColumnConfig represents a column header in the comparison table
|
|
type ComparisonColumnConfig struct {
|
|
Name string `yaml:"name,omitempty" json:"name,omitempty"`
|
|
Highlight bool `yaml:"highlight,omitempty" json:"highlight,omitempty"`
|
|
}
|
|
|
|
// ComparisonGroupConfig represents a group of features in the comparison table
|
|
type ComparisonGroupConfig struct {
|
|
Name string `yaml:"name,omitempty" json:"name,omitempty"`
|
|
Features []ComparisonFeatureConfig `yaml:"features,omitempty" json:"features,omitempty"`
|
|
}
|
|
|
|
// ComparisonFeatureConfig represents a single feature row in the comparison table
|
|
type ComparisonFeatureConfig struct {
|
|
Name string `yaml:"name,omitempty" json:"name,omitempty"`
|
|
Values []string `yaml:"values,omitempty" json:"values,omitempty"` // "true"/"false" for check/x, anything else displayed as text
|
|
}
|
|
|
|
// NavigationConfig controls which built-in navigation links appear in the header and footer
|
|
type NavigationConfig struct {
|
|
ShowDocs bool `yaml:"show_docs,omitempty" json:"show_docs,omitempty"`
|
|
ShowAPI bool `yaml:"show_api,omitempty" json:"show_api,omitempty"`
|
|
ShowRepository bool `yaml:"show_repository,omitempty" json:"show_repository,omitempty"`
|
|
ShowReleases bool `yaml:"show_releases,omitempty" json:"show_releases,omitempty"`
|
|
ShowIssues bool `yaml:"show_issues,omitempty" json:"show_issues,omitempty"`
|
|
// Translatable labels for nav items and section headers (defaults to English)
|
|
LabelValueProps string `yaml:"label_value_props,omitempty" json:"label_value_props,omitempty"`
|
|
LabelFeatures string `yaml:"label_features,omitempty" json:"label_features,omitempty"`
|
|
LabelPricing string `yaml:"label_pricing,omitempty" json:"label_pricing,omitempty"`
|
|
LabelBlog string `yaml:"label_blog,omitempty" json:"label_blog,omitempty"`
|
|
LabelGallery string `yaml:"label_gallery,omitempty" json:"label_gallery,omitempty"`
|
|
LabelCompare string `yaml:"label_compare,omitempty" json:"label_compare,omitempty"`
|
|
LabelDocs string `yaml:"label_docs,omitempty" json:"label_docs,omitempty"`
|
|
LabelReleases string `yaml:"label_releases,omitempty" json:"label_releases,omitempty"`
|
|
LabelAPI string `yaml:"label_api,omitempty" json:"label_api,omitempty"`
|
|
LabelIssues string `yaml:"label_issues,omitempty" json:"label_issues,omitempty"`
|
|
}
|
|
|
|
// FooterConfig represents footer settings
|
|
type FooterConfig struct {
|
|
Links []FooterLink `yaml:"links,omitempty" json:"links,omitempty"`
|
|
Social []SocialLink `yaml:"social,omitempty" json:"social,omitempty"`
|
|
Copyright string `yaml:"copyright,omitempty" json:"copyright,omitempty"`
|
|
ShowPoweredBy bool `yaml:"show_powered_by,omitempty" json:"show_powered_by,omitempty"`
|
|
}
|
|
|
|
// FooterLink represents a single footer link
|
|
type FooterLink struct {
|
|
Label string `yaml:"label,omitempty" json:"label,omitempty"`
|
|
URL string `yaml:"url,omitempty" json:"url,omitempty"`
|
|
}
|
|
|
|
// SocialLink represents a social media link
|
|
type SocialLink struct {
|
|
Platform string `yaml:"platform,omitempty" json:"platform,omitempty"` // bluesky, discord, facebook, github, instagram, linkedin, mastodon, reddit, rss, substack, threads, tiktok, twitch, twitter, youtube
|
|
URL string `yaml:"url,omitempty" json:"url,omitempty"`
|
|
}
|
|
|
|
// ThemeConfig represents theme customization
|
|
type ThemeConfig struct {
|
|
PrimaryColor string `yaml:"primary_color,omitempty" json:"primary_color,omitempty"`
|
|
AccentColor string `yaml:"accent_color,omitempty" json:"accent_color,omitempty"`
|
|
Mode string `yaml:"mode,omitempty" json:"mode,omitempty"` // light, dark, auto
|
|
}
|
|
|
|
// SEOConfig represents SEO and social sharing settings
|
|
type SEOConfig struct {
|
|
Title string `yaml:"title,omitempty" json:"title,omitempty"`
|
|
Description string `yaml:"description,omitempty" json:"description,omitempty"`
|
|
Keywords []string `yaml:"keywords,omitempty" json:"keywords,omitempty"`
|
|
OGImage string `yaml:"og_image,omitempty" json:"og_image,omitempty"`
|
|
UseMediaKitOG bool `yaml:"use_media_kit_og,omitempty" json:"use_media_kit_og,omitempty"`
|
|
TwitterCard string `yaml:"twitter_card,omitempty" json:"twitter_card,omitempty"`
|
|
TwitterSite string `yaml:"twitter_site,omitempty" json:"twitter_site,omitempty"`
|
|
}
|
|
|
|
// AnalyticsConfig represents analytics settings
|
|
type AnalyticsConfig struct {
|
|
Plausible string `yaml:"plausible,omitempty" json:"plausible,omitempty"`
|
|
Umami UmamiConfig `yaml:"umami,omitempty" json:"umami,omitzero"`
|
|
GoogleAnalytics string `yaml:"google_analytics,omitempty" json:"google_analytics,omitempty"`
|
|
}
|
|
|
|
// UmamiConfig represents Umami analytics settings
|
|
type UmamiConfig struct {
|
|
WebsiteID string `yaml:"website_id,omitempty" json:"website_id,omitempty"`
|
|
URL string `yaml:"url,omitempty" json:"url,omitempty"`
|
|
}
|
|
|
|
// ExperimentConfig represents A/B testing experiment settings
|
|
type ExperimentConfig struct {
|
|
Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
|
|
AutoOptimize bool `yaml:"auto_optimize,omitempty" json:"auto_optimize,omitempty"`
|
|
MinImpressions int `yaml:"min_impressions,omitempty" json:"min_impressions,omitempty"`
|
|
ApprovalRequired bool `yaml:"approval_required,omitempty" json:"approval_required,omitempty"`
|
|
}
|
|
|
|
// I18nConfig represents multi-language settings for the landing page
|
|
type I18nConfig struct {
|
|
DefaultLang string `yaml:"default_lang,omitempty" json:"default_lang,omitempty"`
|
|
Languages []string `yaml:"languages,omitempty" json:"languages,omitempty"`
|
|
}
|
|
|
|
// LanguageDisplayNames returns a map of language codes to native display names
|
|
func LanguageDisplayNames() map[string]string {
|
|
return map[string]string{
|
|
"en": "English",
|
|
"es": "Español",
|
|
"de": "Deutsch",
|
|
"fr": "Français",
|
|
"ja": "日本語",
|
|
"zh": "中文",
|
|
"pt": "Português",
|
|
"ru": "Русский",
|
|
"ko": "한국어",
|
|
"it": "Italiano",
|
|
"hi": "हिन्दी",
|
|
"ar": "العربية",
|
|
"nl": "Nederlands",
|
|
"pl": "Polski",
|
|
"tr": "Türkçe",
|
|
}
|
|
}
|
|
|
|
// AdvancedConfig represents advanced settings
|
|
type AdvancedConfig struct {
|
|
CustomCSS string `yaml:"custom_css,omitempty" json:"custom_css,omitempty"`
|
|
CustomHead string `yaml:"custom_head,omitempty" json:"custom_head,omitempty"`
|
|
Redirects map[string]string `yaml:"redirects,omitempty" json:"redirects,omitempty"`
|
|
StaticRoutes []string `yaml:"static_routes,omitempty" json:"static_routes,omitempty"`
|
|
PublicReleases bool `yaml:"public_releases,omitempty" json:"public_releases,omitempty"`
|
|
HideMobileReleases bool `yaml:"hide_mobile_releases,omitempty" json:"hide_mobile_releases,omitempty"`
|
|
GooglePlayID string `yaml:"google_play_id,omitempty" json:"google_play_id,omitempty"`
|
|
AppStoreID string `yaml:"app_store_id,omitempty" json:"app_store_id,omitempty"`
|
|
}
|
|
|
|
// ParseLandingConfig parses a landing.yaml file content
|
|
func ParseLandingConfig(content []byte) (*LandingConfig, error) {
|
|
config := &LandingConfig{
|
|
Enabled: true,
|
|
Template: "open-source-hero",
|
|
}
|
|
|
|
if err := yaml.Unmarshal(content, config); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Apply defaults
|
|
if config.Template == "" {
|
|
config.Template = "open-source-hero"
|
|
}
|
|
if config.Theme.Mode == "" {
|
|
config.Theme.Mode = "auto"
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
// HashConfig returns a SHA256 hash of the config content for change detection
|
|
func HashConfig(content []byte) string {
|
|
hash := sha256.Sum256(content)
|
|
return hex.EncodeToString(hash[:])
|
|
}
|
|
|
|
// DefaultConfig returns a default landing page configuration
|
|
func DefaultConfig() *LandingConfig {
|
|
return &LandingConfig{
|
|
Enabled: true,
|
|
Template: "open-source-hero",
|
|
Hero: HeroConfig{
|
|
Headline: "Build something amazing",
|
|
Subheadline: "A powerful toolkit for developers who want to ship fast.",
|
|
PrimaryCTA: CTAButton{
|
|
Label: "Get Started",
|
|
URL: "#",
|
|
},
|
|
SecondaryCTA: CTAButton{
|
|
Label: "View on GitHub",
|
|
URL: "#",
|
|
},
|
|
},
|
|
Stats: []StatConfig{
|
|
{Value: "10k+", Label: "Downloads"},
|
|
{Value: "100+", Label: "Contributors"},
|
|
{Value: "MIT", Label: "License"},
|
|
},
|
|
ValueProps: []ValuePropConfig{
|
|
{Title: "Fast", Description: "Optimized for performance out of the box.", Icon: "zap"},
|
|
{Title: "Flexible", Description: "Adapts to your workflow, not the other way around.", Icon: "gear"},
|
|
{Title: "Open Source", Description: "Free forever. Community driven.", Icon: "heart"},
|
|
},
|
|
Navigation: NavigationConfig{
|
|
ShowDocs: true,
|
|
ShowRepository: true,
|
|
ShowReleases: true,
|
|
},
|
|
CTASection: CTASectionConfig{
|
|
Headline: "Ready to get started?",
|
|
Subheadline: "Join thousands of developers already using this project.",
|
|
Button: CTAButton{
|
|
Label: "Get Started Free",
|
|
URL: "#",
|
|
},
|
|
},
|
|
Footer: FooterConfig{
|
|
ShowPoweredBy: true,
|
|
},
|
|
Theme: ThemeConfig{
|
|
Mode: "auto",
|
|
},
|
|
}
|
|
}
|
|
|
|
// ValidTemplates returns the list of valid template names
|
|
func ValidTemplates() []string {
|
|
return []string{"open-source-hero", "minimalist-docs", "saas-conversion", "bold-marketing", "documentation-first", "developer-tool", "visual-showcase", "cli-terminal", "architecture-deep-dive"}
|
|
}
|
|
|
|
// IsValidTemplate checks if a template name is valid
|
|
func IsValidTemplate(name string) bool {
|
|
return slices.Contains(ValidTemplates(), name)
|
|
}
|
|
|
|
// TemplateDisplayNames returns a map of template names to display names
|
|
func TemplateDisplayNames() map[string]string {
|
|
return map[string]string{
|
|
"open-source-hero": "Open Source Product",
|
|
"minimalist-docs": "Minimalist Product",
|
|
"saas-conversion": "SaaS Product",
|
|
"bold-marketing": "Bold Marketing Product",
|
|
"documentation-first": "Documentation First",
|
|
"developer-tool": "Developer Tool",
|
|
"visual-showcase": "Visual Showcase",
|
|
"cli-terminal": "CLI Terminal",
|
|
"architecture-deep-dive": "Architecture Deep Dive",
|
|
}
|
|
}
|
|
|
|
// TemplateDefaultLabels returns the template-specific default section labels.
|
|
// These are the creative names each template uses for its sections.
|
|
func TemplateDefaultLabels(template string) NavigationConfig {
|
|
switch template {
|
|
case "architecture-deep-dive":
|
|
return NavigationConfig{
|
|
LabelValueProps: "Systems Analysis",
|
|
LabelFeatures: "Technical Specifications",
|
|
LabelPricing: "Resource Allocation",
|
|
LabelBlog: "Dispatches",
|
|
LabelGallery: "Visual Index",
|
|
LabelCompare: "Compare",
|
|
}
|
|
case "bold-marketing":
|
|
return NavigationConfig{
|
|
LabelValueProps: "Why choose this",
|
|
LabelFeatures: "Capabilities",
|
|
LabelPricing: "Investment",
|
|
LabelBlog: "Blog",
|
|
LabelGallery: "Gallery",
|
|
LabelCompare: "Compare",
|
|
}
|
|
case "minimalist-docs":
|
|
return NavigationConfig{
|
|
LabelValueProps: "Why choose this",
|
|
LabelFeatures: "Capabilities",
|
|
LabelPricing: "Investment",
|
|
LabelBlog: "Blog",
|
|
LabelGallery: "Gallery",
|
|
LabelCompare: "Compare",
|
|
}
|
|
case "open-source-hero":
|
|
return NavigationConfig{
|
|
LabelValueProps: "Why choose us",
|
|
LabelFeatures: "Capabilities",
|
|
LabelPricing: "Pricing",
|
|
LabelBlog: "Blog",
|
|
LabelGallery: "Gallery",
|
|
LabelCompare: "Compare",
|
|
}
|
|
case "saas-conversion":
|
|
return NavigationConfig{
|
|
LabelValueProps: "Why",
|
|
LabelFeatures: "Features",
|
|
LabelPricing: "Pricing",
|
|
LabelBlog: "Blog",
|
|
LabelGallery: "Gallery",
|
|
LabelCompare: "Compare",
|
|
}
|
|
default:
|
|
// developer-tool, documentation-first, visual-showcase, cli-terminal
|
|
return NavigationConfig{
|
|
LabelValueProps: "Why choose us",
|
|
LabelFeatures: "Capabilities",
|
|
LabelPricing: "Pricing",
|
|
LabelBlog: "Blog",
|
|
LabelGallery: "Gallery",
|
|
LabelCompare: "Compare",
|
|
}
|
|
}
|
|
}
|