Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c5daac3366 | |||
| 916211004d | |||
| 02fdc1a194 | |||
| 1b0bba09b9 | |||
| 0c0d1c1493 | |||
| 9461599b57 | |||
| 414560f470 | |||
| b43345986a | |||
| 7fbbd26b20 | |||
| b26bf4bfe8 | |||
| 242ebf2dc1 | |||
| 46f7570d25 | |||
| 48aab974fe | |||
| 965ef8966f | |||
| 17028589c8 | |||
| 80096bfbf9 | |||
| 22844f6437 | |||
| 737e323fcb | |||
| 00024298d0 | |||
| b27dd0cda8 | |||
| 43e490d933 | |||
| d02f25c0ba | |||
| 12341079e1 | |||
| c5e35e3466 | |||
| c0fcf16794 | |||
| c5786aab2b | |||
| fe5b504b97 | |||
| a1f477a381 |
@@ -16,7 +16,7 @@ env:
|
||||
GOPRIVATE: git.marketally.com
|
||||
GONOSUMDB: git.marketally.com
|
||||
GOTOOLCHAIN: local
|
||||
GO_VERSION: "1.25.0"
|
||||
GO_VERSION: "1.25.5"
|
||||
NODE_VERSION: "22"
|
||||
|
||||
jobs:
|
||||
@@ -24,6 +24,8 @@ jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: linux-latest
|
||||
env:
|
||||
GOMODCACHE: /tmp/gomod-${{ github.run_id }}-lint
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@@ -51,7 +53,8 @@ jobs:
|
||||
run: npm install -g pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: make deps-frontend deps-backend
|
||||
run: |
|
||||
make deps-frontend deps-backend
|
||||
|
||||
- name: Run Go linter
|
||||
run: make lint-go
|
||||
@@ -64,6 +67,8 @@ jobs:
|
||||
test-unit:
|
||||
name: Unit Tests
|
||||
runs-on: linux-latest
|
||||
env:
|
||||
GOMODCACHE: /tmp/gomod-${{ github.run_id }}-unit
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@@ -83,7 +88,8 @@ jobs:
|
||||
cache: false
|
||||
|
||||
- name: Install dependencies
|
||||
run: go mod download
|
||||
run: |
|
||||
go mod download
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
@@ -99,6 +105,8 @@ jobs:
|
||||
test-pgsql:
|
||||
name: Integration Tests (PostgreSQL)
|
||||
runs-on: linux-latest
|
||||
env:
|
||||
GOMODCACHE: /tmp/gomod-${{ github.run_id }}-pgsql
|
||||
services:
|
||||
pgsql:
|
||||
image: postgres:15
|
||||
@@ -140,7 +148,8 @@ jobs:
|
||||
run: npm install -g pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: make deps-frontend deps-backend
|
||||
run: |
|
||||
make deps-frontend deps-backend
|
||||
|
||||
- name: Build frontend
|
||||
run: make frontend
|
||||
@@ -378,7 +387,8 @@ jobs:
|
||||
|
||||
- name: Install dependencies (Unix)
|
||||
if: matrix.goos != 'windows'
|
||||
run: make deps-frontend deps-backend
|
||||
run: |
|
||||
make deps-frontend deps-backend
|
||||
|
||||
- name: Install dependencies (Windows)
|
||||
if: matrix.goos == 'windows'
|
||||
|
||||
3
go.mod
3
go.mod
@@ -326,6 +326,9 @@ replace github.com/go-ini/ini => github.com/go-ini/ini v1.66.6
|
||||
// Use GitCaddy fork with capability support
|
||||
replace code.gitea.io/actions-proto-go => git.marketally.com/gitcaddy/actions-proto-go v0.5.8
|
||||
|
||||
// Mirror of deleted github.com/hexops/gotextdiff
|
||||
replace github.com/hexops/gotextdiff => git.marketally.com/mirrors/gotextdiff v1.0.3
|
||||
|
||||
// Vault plugin - use local directory for development
|
||||
replace git.marketally.com/gitcaddy/gitcaddy-vault => ../gitcaddy-vault
|
||||
|
||||
|
||||
4
go.sum
4
go.sum
@@ -31,6 +31,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
git.marketally.com/gitcaddy/actions-proto-go v0.5.8 h1:MBipeHvY6A0jcobvziUtzgatZTrV4fs/HE1rPQxREN4=
|
||||
git.marketally.com/gitcaddy/actions-proto-go v0.5.8/go.mod h1:RPu21UoRD3zSAujoZR6LJwuVNa2uFRBveadslczCRfQ=
|
||||
git.marketally.com/mirrors/gotextdiff v1.0.3 h1:Mxf+YurdCHT4y1GNiZCTDWYtVXSxhlLUeG7g7i9Za70=
|
||||
git.marketally.com/mirrors/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763 h1:ohdxegvslDEllZmRNDqpKun6L4Oq81jNdEDtGgHEV2c=
|
||||
gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok=
|
||||
gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:BAFmdZpRW7zMQZQDClaCWobRj9uL1MR3MzpCVJvc5s4=
|
||||
@@ -490,8 +492,6 @@ github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
|
||||
@@ -170,6 +170,13 @@ const (
|
||||
WishlistVoteBudget ErrorCode = "WISHLIST_VOTE_BUDGET_EXCEEDED"
|
||||
)
|
||||
|
||||
// Pages errors (PAGES_)
|
||||
const (
|
||||
PagesNotConfigured ErrorCode = "PAGES_NOT_CONFIGURED"
|
||||
PagesNotEnabled ErrorCode = "PAGES_NOT_ENABLED"
|
||||
PagesInvalidTemplate ErrorCode = "PAGES_INVALID_TEMPLATE"
|
||||
)
|
||||
|
||||
// AI errors (AI_)
|
||||
const (
|
||||
AIDisabled ErrorCode = "AI_DISABLED"
|
||||
@@ -310,6 +317,11 @@ var errorCatalog = map[ErrorCode]errorInfo{
|
||||
WishlistDisabled: {"Wishlist is disabled for this repository", http.StatusForbidden},
|
||||
WishlistVoteBudget: {"Vote budget exceeded for this repository", http.StatusConflict},
|
||||
|
||||
// Pages errors
|
||||
PagesNotConfigured: {"Landing page is not configured for this repository", http.StatusNotFound},
|
||||
PagesNotEnabled: {"Landing page is not enabled for this repository", http.StatusForbidden},
|
||||
PagesInvalidTemplate: {"Invalid landing page template name", http.StatusBadRequest},
|
||||
|
||||
// AI errors
|
||||
AIDisabled: {"AI features are disabled", http.StatusForbidden},
|
||||
AIUnitNotEnabled: {"AI unit is not enabled for this repository", http.StatusForbidden},
|
||||
|
||||
@@ -13,80 +13,80 @@ import (
|
||||
|
||||
// LandingConfig represents the parsed .gitea/landing.yaml configuration
|
||||
type LandingConfig struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
PublicLanding bool `yaml:"public_landing"`
|
||||
Template string `yaml:"template"` // open-source-hero, minimalist-docs, saas-conversion, bold-marketing
|
||||
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"`
|
||||
Domain string `yaml:"domain,omitempty" json:"domain,omitempty"`
|
||||
|
||||
// Brand configuration
|
||||
Brand BrandConfig `yaml:"brand,omitempty"`
|
||||
Brand BrandConfig `yaml:"brand,omitempty" json:"brand,omitzero"`
|
||||
|
||||
// Hero section
|
||||
Hero HeroConfig `yaml:"hero,omitempty"`
|
||||
Hero HeroConfig `yaml:"hero,omitempty" json:"hero,omitzero"`
|
||||
|
||||
// Stats/metrics
|
||||
Stats []StatConfig `yaml:"stats,omitempty"`
|
||||
Stats []StatConfig `yaml:"stats,omitempty" json:"stats,omitempty"`
|
||||
|
||||
// Value propositions
|
||||
ValueProps []ValuePropConfig `yaml:"value_props,omitempty"`
|
||||
ValueProps []ValuePropConfig `yaml:"value_props,omitempty" json:"value_props,omitempty"`
|
||||
|
||||
// Features
|
||||
Features []FeatureConfig `yaml:"features,omitempty"`
|
||||
Features []FeatureConfig `yaml:"features,omitempty" json:"features,omitempty"`
|
||||
|
||||
// Social proof
|
||||
SocialProof SocialProofConfig `yaml:"social_proof,omitempty"`
|
||||
SocialProof SocialProofConfig `yaml:"social_proof,omitempty" json:"social_proof,omitzero"`
|
||||
|
||||
// Pricing (for saas-conversion template)
|
||||
Pricing PricingConfig `yaml:"pricing,omitempty"`
|
||||
Pricing PricingConfig `yaml:"pricing,omitempty" json:"pricing,omitzero"`
|
||||
|
||||
// CTA section
|
||||
CTASection CTASectionConfig `yaml:"cta_section,omitempty"`
|
||||
CTASection CTASectionConfig `yaml:"cta_section,omitempty" json:"cta_section,omitzero"`
|
||||
|
||||
// Blog section
|
||||
Blog BlogSectionConfig `yaml:"blog,omitempty"`
|
||||
Blog BlogSectionConfig `yaml:"blog,omitempty" json:"blog,omitzero"`
|
||||
|
||||
// Gallery section
|
||||
Gallery GallerySectionConfig `yaml:"gallery,omitempty"`
|
||||
Gallery GallerySectionConfig `yaml:"gallery,omitempty" json:"gallery,omitzero"`
|
||||
|
||||
// Comparison section
|
||||
Comparison ComparisonSectionConfig `yaml:"comparison,omitempty"`
|
||||
Comparison ComparisonSectionConfig `yaml:"comparison,omitempty" json:"comparison,omitzero"`
|
||||
|
||||
// Navigation visibility
|
||||
Navigation NavigationConfig `yaml:"navigation,omitempty"`
|
||||
Navigation NavigationConfig `yaml:"navigation,omitempty" json:"navigation,omitzero"`
|
||||
|
||||
// Footer
|
||||
Footer FooterConfig `yaml:"footer,omitempty"`
|
||||
Footer FooterConfig `yaml:"footer,omitempty" json:"footer,omitzero"`
|
||||
|
||||
// Theme customization
|
||||
Theme ThemeConfig `yaml:"theme,omitempty"`
|
||||
Theme ThemeConfig `yaml:"theme,omitempty" json:"theme,omitzero"`
|
||||
|
||||
// SEO & Social
|
||||
SEO SEOConfig `yaml:"seo,omitempty"`
|
||||
SEO SEOConfig `yaml:"seo,omitempty" json:"seo,omitzero"`
|
||||
|
||||
// Analytics
|
||||
Analytics AnalyticsConfig `yaml:"analytics,omitempty"`
|
||||
Analytics AnalyticsConfig `yaml:"analytics,omitempty" json:"analytics,omitzero"`
|
||||
|
||||
// Advanced settings
|
||||
Advanced AdvancedConfig `yaml:"advanced,omitempty"`
|
||||
Advanced AdvancedConfig `yaml:"advanced,omitempty" json:"advanced,omitzero"`
|
||||
|
||||
// A/B testing experiments
|
||||
Experiments ExperimentConfig `yaml:"experiments,omitempty"`
|
||||
Experiments ExperimentConfig `yaml:"experiments,omitempty" json:"experiments,omitzero"`
|
||||
|
||||
// Multi-language support
|
||||
I18n I18nConfig `yaml:"i18n,omitempty"`
|
||||
I18n I18nConfig `yaml:"i18n,omitempty" json:"i18n,omitzero"`
|
||||
}
|
||||
|
||||
// BrandConfig represents brand/identity settings
|
||||
type BrandConfig struct {
|
||||
Name string `yaml:"name,omitempty"`
|
||||
LogoURL string `yaml:"logo_url,omitempty"`
|
||||
UploadedLogo string `yaml:"uploaded_logo,omitempty"`
|
||||
LogoSource string `yaml:"logo_source,omitempty"` // "url" (default), "repo", or "org" — selects avatar source
|
||||
Tagline string `yaml:"tagline,omitempty"`
|
||||
FaviconURL string `yaml:"favicon_url,omitempty"`
|
||||
UploadedFavicon string `yaml:"uploaded_favicon,omitempty"`
|
||||
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.
|
||||
@@ -107,14 +107,14 @@ func (b *BrandConfig) ResolvedFaviconURL() string {
|
||||
|
||||
// HeroConfig represents hero section settings
|
||||
type HeroConfig struct {
|
||||
Headline string `yaml:"headline,omitempty"`
|
||||
Subheadline string `yaml:"subheadline,omitempty"`
|
||||
PrimaryCTA CTAButton `yaml:"primary_cta,omitempty"`
|
||||
SecondaryCTA CTAButton `yaml:"secondary_cta,omitempty"`
|
||||
ImageURL string `yaml:"image_url,omitempty"`
|
||||
UploadedImage string `yaml:"uploaded_image,omitempty"` // filename in repo-avatars storage
|
||||
CodeExample string `yaml:"code_example,omitempty"`
|
||||
VideoURL string `yaml:"video_url,omitempty"`
|
||||
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.
|
||||
@@ -127,97 +127,97 @@ func (h *HeroConfig) ResolvedImageURL() string {
|
||||
|
||||
// CTAButton represents a call-to-action button
|
||||
type CTAButton struct {
|
||||
Label string `yaml:"label,omitempty"`
|
||||
URL string `yaml:"url,omitempty"`
|
||||
Variant string `yaml:"variant,omitempty"` // primary, secondary, outline, text
|
||||
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"`
|
||||
Label string `yaml:"label,omitempty"`
|
||||
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"`
|
||||
Description string `yaml:"description,omitempty"`
|
||||
Icon string `yaml:"icon,omitempty"`
|
||||
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"`
|
||||
Description string `yaml:"description,omitempty"`
|
||||
Icon string `yaml:"icon,omitempty"`
|
||||
ImageURL string `yaml:"image_url,omitempty"`
|
||||
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"`
|
||||
Testimonial TestimonialConfig `yaml:"testimonial,omitempty"`
|
||||
Testimonials []TestimonialConfig `yaml:"testimonials,omitempty"`
|
||||
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"`
|
||||
Author string `yaml:"author,omitempty"`
|
||||
Role string `yaml:"role,omitempty"`
|
||||
Avatar string `yaml:"avatar,omitempty"`
|
||||
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"`
|
||||
Subheadline string `yaml:"subheadline,omitempty"`
|
||||
Plans []PricingPlanConfig `yaml:"plans,omitempty"`
|
||||
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"`
|
||||
Price string `yaml:"price,omitempty"`
|
||||
Period string `yaml:"period,omitempty"`
|
||||
Features []string `yaml:"features,omitempty"`
|
||||
CTA string `yaml:"cta,omitempty"`
|
||||
Featured bool `yaml:"featured,omitempty"`
|
||||
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"`
|
||||
Subheadline string `yaml:"subheadline,omitempty"`
|
||||
Button CTAButton `yaml:"button,omitempty"`
|
||||
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"`
|
||||
Headline string `yaml:"headline,omitempty"`
|
||||
Subheadline string `yaml:"subheadline,omitempty"`
|
||||
MaxPosts int `yaml:"max_posts,omitempty"` // default 3
|
||||
ShowExcerpt bool `yaml:"show_excerpt,omitempty"` // show subtitle as excerpt
|
||||
CTAButton CTAButton `yaml:"cta_button,omitempty"` // "View All Posts" link
|
||||
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"`
|
||||
Headline string `yaml:"headline,omitempty"`
|
||||
Subheadline string `yaml:"subheadline,omitempty"`
|
||||
MaxImages int `yaml:"max_images,omitempty"` // default 6
|
||||
Columns int `yaml:"columns,omitempty"` // grid columns, default 3
|
||||
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"`
|
||||
Headline string `yaml:"headline,omitempty"`
|
||||
Subheadline string `yaml:"subheadline,omitempty"`
|
||||
Columns []ComparisonColumnConfig `yaml:"columns,omitempty"`
|
||||
Groups []ComparisonGroupConfig `yaml:"groups,omitempty"`
|
||||
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
|
||||
@@ -235,88 +235,99 @@ func (c *ComparisonSectionConfig) HasData() bool {
|
||||
|
||||
// ComparisonColumnConfig represents a column header in the comparison table
|
||||
type ComparisonColumnConfig struct {
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Highlight bool `yaml:"highlight,omitempty"`
|
||||
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"`
|
||||
Features []ComparisonFeatureConfig `yaml:"features,omitempty"`
|
||||
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"`
|
||||
Values []string `yaml:"values,omitempty"` // "true"/"false" for check/x, anything else displayed as text
|
||||
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"`
|
||||
ShowAPI bool `yaml:"show_api,omitempty"`
|
||||
ShowRepository bool `yaml:"show_repository,omitempty"`
|
||||
ShowReleases bool `yaml:"show_releases,omitempty"`
|
||||
ShowIssues bool `yaml:"show_issues,omitempty"`
|
||||
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"`
|
||||
Social []SocialLink `yaml:"social,omitempty"`
|
||||
Copyright string `yaml:"copyright,omitempty"`
|
||||
ShowPoweredBy bool `yaml:"show_powered_by,omitempty"`
|
||||
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"`
|
||||
URL string `yaml:"url,omitempty"`
|
||||
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"` // bluesky, discord, facebook, github, instagram, linkedin, mastodon, reddit, rss, substack, threads, tiktok, twitch, twitter, youtube
|
||||
URL string `yaml:"url,omitempty"`
|
||||
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"`
|
||||
AccentColor string `yaml:"accent_color,omitempty"`
|
||||
Mode string `yaml:"mode,omitempty"` // light, dark, auto
|
||||
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"`
|
||||
Description string `yaml:"description,omitempty"`
|
||||
Keywords []string `yaml:"keywords,omitempty"`
|
||||
OGImage string `yaml:"og_image,omitempty"`
|
||||
UseMediaKitOG bool `yaml:"use_media_kit_og,omitempty"`
|
||||
TwitterCard string `yaml:"twitter_card,omitempty"`
|
||||
TwitterSite string `yaml:"twitter_site,omitempty"`
|
||||
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"`
|
||||
Umami UmamiConfig `yaml:"umami,omitempty"`
|
||||
GoogleAnalytics string `yaml:"google_analytics,omitempty"`
|
||||
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"`
|
||||
URL string `yaml:"url,omitempty"`
|
||||
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"`
|
||||
AutoOptimize bool `yaml:"auto_optimize,omitempty"`
|
||||
MinImpressions int `yaml:"min_impressions,omitempty"`
|
||||
ApprovalRequired bool `yaml:"approval_required,omitempty"`
|
||||
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
|
||||
@@ -325,36 +336,37 @@ type I18nConfig struct {
|
||||
Languages []string `yaml:"languages,omitempty" json:"languages,omitempty"`
|
||||
}
|
||||
|
||||
// LanguageDisplayNames returns a map of language codes to display names
|
||||
// LanguageDisplayNames returns a map of language codes to native display names
|
||||
func LanguageDisplayNames() map[string]string {
|
||||
return map[string]string{
|
||||
"en": "English",
|
||||
"es": "Espanol",
|
||||
"es": "Español",
|
||||
"de": "Deutsch",
|
||||
"fr": "Francais",
|
||||
"ja": "Japanese",
|
||||
"zh": "Chinese",
|
||||
"pt": "Portugues",
|
||||
"ru": "Russian",
|
||||
"ko": "Korean",
|
||||
"fr": "Français",
|
||||
"ja": "日本語",
|
||||
"zh": "中文",
|
||||
"pt": "Português",
|
||||
"ru": "Русский",
|
||||
"ko": "한국어",
|
||||
"it": "Italiano",
|
||||
"hi": "Hindi",
|
||||
"ar": "Arabic",
|
||||
"hi": "हिन्दी",
|
||||
"ar": "العربية",
|
||||
"nl": "Nederlands",
|
||||
"pl": "Polski",
|
||||
"tr": "Turkish",
|
||||
"tr": "Türkçe",
|
||||
}
|
||||
}
|
||||
|
||||
// AdvancedConfig represents advanced settings
|
||||
type AdvancedConfig struct {
|
||||
CustomCSS string `yaml:"custom_css,omitempty"`
|
||||
CustomHead string `yaml:"custom_head,omitempty"`
|
||||
Redirects map[string]string `yaml:"redirects,omitempty"`
|
||||
PublicReleases bool `yaml:"public_releases,omitempty"`
|
||||
HideMobileReleases bool `yaml:"hide_mobile_releases,omitempty"`
|
||||
GooglePlayID string `yaml:"google_play_id,omitempty"`
|
||||
AppStoreID string `yaml:"app_store_id,omitempty"`
|
||||
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
|
||||
@@ -458,3 +470,65 @@ func TemplateDisplayNames() map[string]string {
|
||||
"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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,3 +47,231 @@ type PagesInfo struct {
|
||||
Config *PagesConfig `json:"config"`
|
||||
Domains []*PagesDomain `json:"domains,omitempty"`
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// v2 Landing Page Configuration API structs
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// UpdatePagesConfigOption represents the full landing page config update.
|
||||
// For PATCH, only non-nil fields are applied. For PUT, all fields replace existing config.
|
||||
type UpdatePagesConfigOption struct {
|
||||
Enabled *bool `json:"enabled"`
|
||||
PublicLanding *bool `json:"public_landing"`
|
||||
Template *string `json:"template"`
|
||||
Brand *UpdatePagesBrandOption `json:"brand"`
|
||||
Hero *UpdatePagesHeroOption `json:"hero"`
|
||||
Stats *[]PagesStatOption `json:"stats"`
|
||||
ValueProps *[]PagesValuePropOption `json:"value_props"`
|
||||
Features *[]PagesFeatureOption `json:"features"`
|
||||
SocialProof *UpdatePagesSocialOption `json:"social_proof"`
|
||||
Pricing *UpdatePagesPricingOption `json:"pricing"`
|
||||
CTASection *UpdatePagesCTAOption `json:"cta_section"`
|
||||
Blog *UpdatePagesBlogOption `json:"blog"`
|
||||
Gallery *UpdatePagesGalleryOption `json:"gallery"`
|
||||
Comparison *UpdatePagesComparisonOption `json:"comparison"`
|
||||
Navigation *UpdatePagesNavOption `json:"navigation"`
|
||||
Footer *UpdatePagesFooterOption `json:"footer"`
|
||||
Theme *UpdatePagesThemeOption `json:"theme"`
|
||||
SEO *UpdatePagesSEOOption `json:"seo"`
|
||||
Advanced *UpdatePagesAdvancedOption `json:"advanced"`
|
||||
}
|
||||
|
||||
// UpdatePagesBrandOption represents brand section update
|
||||
type UpdatePagesBrandOption struct {
|
||||
Name *string `json:"name"`
|
||||
LogoURL *string `json:"logo_url"`
|
||||
Tagline *string `json:"tagline"`
|
||||
FaviconURL *string `json:"favicon_url"`
|
||||
}
|
||||
|
||||
// UpdatePagesHeroOption represents hero section update
|
||||
type UpdatePagesHeroOption struct {
|
||||
Headline *string `json:"headline"`
|
||||
Subheadline *string `json:"subheadline"`
|
||||
ImageURL *string `json:"image_url"`
|
||||
VideoURL *string `json:"video_url"`
|
||||
CodeExample *string `json:"code_example"`
|
||||
PrimaryCTA *PagesCTAButtonOption `json:"primary_cta"`
|
||||
SecondaryCTA *PagesCTAButtonOption `json:"secondary_cta"`
|
||||
}
|
||||
|
||||
// PagesCTAButtonOption represents a CTA button
|
||||
type PagesCTAButtonOption struct {
|
||||
Label *string `json:"label"`
|
||||
URL *string `json:"url"`
|
||||
Variant *string `json:"variant"`
|
||||
}
|
||||
|
||||
// PagesStatOption represents a stat item
|
||||
type PagesStatOption struct {
|
||||
Value string `json:"value"`
|
||||
Label string `json:"label"`
|
||||
}
|
||||
|
||||
// PagesValuePropOption represents a value proposition item
|
||||
type PagesValuePropOption struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
}
|
||||
|
||||
// PagesFeatureOption represents a feature item
|
||||
type PagesFeatureOption struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
ImageURL string `json:"image_url"`
|
||||
}
|
||||
|
||||
// UpdatePagesSocialOption represents social proof section update
|
||||
type UpdatePagesSocialOption struct {
|
||||
Logos *[]string `json:"logos"`
|
||||
Testimonials *[]PagesTestimonialOption `json:"testimonials"`
|
||||
}
|
||||
|
||||
// PagesTestimonialOption represents a testimonial item
|
||||
type PagesTestimonialOption struct {
|
||||
Quote string `json:"quote"`
|
||||
Author string `json:"author"`
|
||||
Role string `json:"role"`
|
||||
Avatar string `json:"avatar"`
|
||||
}
|
||||
|
||||
// UpdatePagesPricingOption represents pricing section update
|
||||
type UpdatePagesPricingOption struct {
|
||||
Headline *string `json:"headline"`
|
||||
Subheadline *string `json:"subheadline"`
|
||||
Plans *[]PagesPricingPlanOption `json:"plans"`
|
||||
}
|
||||
|
||||
// PagesPricingPlanOption represents a pricing plan item
|
||||
type PagesPricingPlanOption struct {
|
||||
Name string `json:"name"`
|
||||
Price string `json:"price"`
|
||||
Period string `json:"period"`
|
||||
Features []string `json:"features"`
|
||||
CTA string `json:"cta"`
|
||||
Featured bool `json:"featured"`
|
||||
}
|
||||
|
||||
// UpdatePagesCTAOption represents CTA section update
|
||||
type UpdatePagesCTAOption struct {
|
||||
Headline *string `json:"headline"`
|
||||
Subheadline *string `json:"subheadline"`
|
||||
Button *PagesCTAButtonOption `json:"button"`
|
||||
}
|
||||
|
||||
// UpdatePagesBlogOption represents blog section update
|
||||
type UpdatePagesBlogOption struct {
|
||||
Enabled *bool `json:"enabled"`
|
||||
Headline *string `json:"headline"`
|
||||
Subheadline *string `json:"subheadline"`
|
||||
MaxPosts *int `json:"max_posts"`
|
||||
}
|
||||
|
||||
// UpdatePagesGalleryOption represents gallery section update
|
||||
type UpdatePagesGalleryOption struct {
|
||||
Enabled *bool `json:"enabled"`
|
||||
Headline *string `json:"headline"`
|
||||
Subheadline *string `json:"subheadline"`
|
||||
MaxImages *int `json:"max_images"`
|
||||
Columns *int `json:"columns"`
|
||||
}
|
||||
|
||||
// UpdatePagesComparisonOption represents comparison section update
|
||||
type UpdatePagesComparisonOption struct {
|
||||
Enabled *bool `json:"enabled"`
|
||||
Headline *string `json:"headline"`
|
||||
Subheadline *string `json:"subheadline"`
|
||||
Columns *[]PagesComparisonColumnOption `json:"columns"`
|
||||
Groups *[]PagesComparisonGroupOption `json:"groups"`
|
||||
}
|
||||
|
||||
// PagesComparisonColumnOption represents a comparison column
|
||||
type PagesComparisonColumnOption struct {
|
||||
Name string `json:"name"`
|
||||
Highlight bool `json:"highlight"`
|
||||
}
|
||||
|
||||
// PagesComparisonGroupOption represents a comparison group
|
||||
type PagesComparisonGroupOption struct {
|
||||
Name string `json:"name"`
|
||||
Features []PagesComparisonFeatureOption `json:"features"`
|
||||
}
|
||||
|
||||
// PagesComparisonFeatureOption represents a comparison feature row
|
||||
type PagesComparisonFeatureOption struct {
|
||||
Name string `json:"name"`
|
||||
Values []string `json:"values"`
|
||||
}
|
||||
|
||||
// UpdatePagesNavOption represents navigation section update
|
||||
type UpdatePagesNavOption struct {
|
||||
ShowDocs *bool `json:"show_docs"`
|
||||
ShowAPI *bool `json:"show_api"`
|
||||
ShowRepository *bool `json:"show_repository"`
|
||||
ShowReleases *bool `json:"show_releases"`
|
||||
ShowIssues *bool `json:"show_issues"`
|
||||
}
|
||||
|
||||
// UpdatePagesFooterOption represents footer section update
|
||||
type UpdatePagesFooterOption struct {
|
||||
Copyright *string `json:"copyright"`
|
||||
ShowPoweredBy *bool `json:"show_powered_by"`
|
||||
Links *[]PagesFooterLinkOption `json:"links"`
|
||||
Social *[]PagesSocialLinkOption `json:"social"`
|
||||
CTASection *UpdatePagesCTAOption `json:"cta_section"`
|
||||
}
|
||||
|
||||
// PagesFooterLinkOption represents a footer link
|
||||
type PagesFooterLinkOption struct {
|
||||
Label string `json:"label"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// PagesSocialLinkOption represents a social link
|
||||
type PagesSocialLinkOption struct {
|
||||
Platform string `json:"platform"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// UpdatePagesThemeOption represents theme section update
|
||||
type UpdatePagesThemeOption struct {
|
||||
PrimaryColor *string `json:"primary_color"`
|
||||
AccentColor *string `json:"accent_color"`
|
||||
Mode *string `json:"mode"`
|
||||
}
|
||||
|
||||
// UpdatePagesSEOOption represents SEO section update
|
||||
type UpdatePagesSEOOption struct {
|
||||
Title *string `json:"title"`
|
||||
Description *string `json:"description"`
|
||||
Keywords *[]string `json:"keywords"`
|
||||
OGImage *string `json:"og_image"`
|
||||
UseMediaKitOG *bool `json:"use_media_kit_og"`
|
||||
TwitterCard *string `json:"twitter_card"`
|
||||
TwitterSite *string `json:"twitter_site"`
|
||||
}
|
||||
|
||||
// UpdatePagesAdvancedOption represents advanced settings update
|
||||
type UpdatePagesAdvancedOption struct {
|
||||
CustomCSS *string `json:"custom_css"`
|
||||
CustomHead *string `json:"custom_head"`
|
||||
StaticRoutes *[]string `json:"static_routes"`
|
||||
PublicReleases *bool `json:"public_releases"`
|
||||
HideMobileReleases *bool `json:"hide_mobile_releases"`
|
||||
GooglePlayID *string `json:"google_play_id"`
|
||||
AppStoreID *string `json:"app_store_id"`
|
||||
}
|
||||
|
||||
// UpdatePagesContentOption bundles content-page sections for PUT /config/content
|
||||
type UpdatePagesContentOption struct {
|
||||
Blog *UpdatePagesBlogOption `json:"blog"`
|
||||
Gallery *UpdatePagesGalleryOption `json:"gallery"`
|
||||
ComparisonEnabled *bool `json:"comparison_enabled"`
|
||||
Stats *[]PagesStatOption `json:"stats"`
|
||||
ValueProps *[]PagesValuePropOption `json:"value_props"`
|
||||
Features *[]PagesFeatureOption `json:"features"`
|
||||
Navigation *UpdatePagesNavOption `json:"navigation"`
|
||||
Advanced *UpdatePagesAdvancedOption `json:"advanced"`
|
||||
}
|
||||
|
||||
@@ -4042,6 +4042,9 @@
|
||||
"repo.settings.pages.translations": "P\u0159eklady",
|
||||
"repo.settings.pages.ai_translate": "AI p\u0159eklad",
|
||||
"repo.settings.pages.ai_translate_success": "P\u0159eklad byl \u00fasp\u011b\u0161n\u011b vygenerov\u00e1n AI. Zkontrolujte a upravte dle pot\u0159eby.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Smazat",
|
||||
"repo.settings.pages.save_translation": "Ulo\u017eit p\u0159eklad",
|
||||
"repo.settings.pages.translation_saved": "P\u0159eklad \u00fasp\u011b\u0161n\u011b ulo\u017een.",
|
||||
@@ -4054,6 +4057,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "Nadpis sekce CTA",
|
||||
"repo.settings.pages.trans_cta_subheadline": "Podnadpis sekce CTA",
|
||||
"repo.settings.pages.trans_cta_button": "\u0160t\u00edtek tla\u010d\u00edtka CTA",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -4353,4 +4393,4 @@
|
||||
"actions.runners.waiting_jobs": "Čekající úlohy",
|
||||
"actions.runners.back_to_runners": "Zpět na runnery",
|
||||
"actions.runners.no_waiting_jobs": "Žádné úlohy nečekají na tento label"
|
||||
}
|
||||
}
|
||||
@@ -4248,6 +4248,9 @@
|
||||
"repo.settings.pages.translations": "\u00dcbersetzungen",
|
||||
"repo.settings.pages.ai_translate": "KI-\u00dcbersetzung",
|
||||
"repo.settings.pages.ai_translate_success": "\u00dcbersetzung wurde erfolgreich von der KI generiert. \u00dcberpr\u00fcfen und bearbeiten Sie sie nach Bedarf.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "L\u00f6schen",
|
||||
"repo.settings.pages.save_translation": "\u00dcbersetzung speichern",
|
||||
"repo.settings.pages.translation_saved": "\u00dcbersetzung erfolgreich gespeichert.",
|
||||
@@ -4260,6 +4263,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "CTA-Abschnitt \u00dcberschrift",
|
||||
"repo.settings.pages.trans_cta_subheadline": "CTA-Abschnitt Unter\u00fcberschrift",
|
||||
"repo.settings.pages.trans_cta_button": "CTA-Button-Beschriftung",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault-Plugin nicht installiert",
|
||||
"repo.vault.plugin_not_installed_desc": "Das Vault-Plugin ist auf diesem Server nicht verfügbar",
|
||||
"repo.vault.secret_limit_reached": "Geheimnis-Limit erreicht",
|
||||
@@ -4429,4 +4469,4 @@
|
||||
"actions.runners.waiting_jobs": "Wartende Jobs",
|
||||
"actions.runners.back_to_runners": "Zurück zu Runners",
|
||||
"actions.runners.no_waiting_jobs": "Keine Jobs warten auf dieses Label"
|
||||
}
|
||||
}
|
||||
@@ -3735,6 +3735,9 @@
|
||||
"repo.settings.pages.translations": "Μεταφράσεις",
|
||||
"repo.settings.pages.ai_translate": "Μετάφραση AI",
|
||||
"repo.settings.pages.ai_translate_success": "Η μετάφραση δημιουργήθηκε επιτυχώς από το AI. Ελέγξτε και επεξεργαστείτε όπως χρειάζεται.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Διαγραφή",
|
||||
"repo.settings.pages.save_translation": "Αποθήκευση μετάφρασης",
|
||||
"repo.settings.pages.translation_saved": "Η μετάφραση αποθηκεύτηκε επιτυχώς.",
|
||||
@@ -3747,6 +3750,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "Τίτλος ενότητας CTA",
|
||||
"repo.settings.pages.trans_cta_subheadline": "Υπότιτλος ενότητας CTA",
|
||||
"repo.settings.pages.trans_cta_button": "Ετικέτα κουμπιού CTA",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -4046,4 +4086,4 @@
|
||||
"actions.runners.waiting_jobs": "Αναμονή εργασιών",
|
||||
"actions.runners.back_to_runners": "Πίσω στους Runners",
|
||||
"actions.runners.no_waiting_jobs": "Δεν υπάρχουν εργασίες σε αναμονή για αυτή την ετικέτα"
|
||||
}
|
||||
}
|
||||
@@ -4572,6 +4572,7 @@
|
||||
"repo.settings.pages.ai_generate": "AI Content Generator",
|
||||
"repo.settings.pages.ai_generate_desc": "Automatically generate landing page content (headline, features, stats, CTAs) from your repository's README and metadata using AI.",
|
||||
"repo.settings.pages.ai_generate_button": "Generate Content with AI",
|
||||
"repo.settings.pages.ai_generating": "Generating content with AI\u2026 This may take a moment.",
|
||||
"repo.settings.pages.ai_generate_success": "Landing page content has been generated successfully. Review and customize it in the other tabs.",
|
||||
"repo.settings.pages.ai_generate_failed": "Failed to generate content with AI. Please try again later or configure the content manually.",
|
||||
"repo.settings.pages.languages": "Languages",
|
||||
@@ -4584,6 +4585,10 @@
|
||||
"repo.settings.pages.translations": "Translations",
|
||||
"repo.settings.pages.ai_translate": "AI Translate",
|
||||
"repo.settings.pages.ai_translate_success": "Translation has been generated successfully by AI. Review and edit as needed.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.ai_translating": "Translating with AI\u2026 This may take a moment.",
|
||||
"repo.settings.pages.delete_translation": "Delete",
|
||||
"repo.settings.pages.save_translation": "Save Translation",
|
||||
"repo.settings.pages.translation_saved": "Translation saved successfully.",
|
||||
@@ -4596,6 +4601,57 @@
|
||||
"repo.settings.pages.trans_cta_headline": "CTA Section Headline",
|
||||
"repo.settings.pages.trans_cta_subheadline": "CTA Section Subheadline",
|
||||
"repo.settings.pages.trans_cta_button": "CTA Button Label",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_section_navigation": "Navigation Labels",
|
||||
"repo.settings.pages.trans_nav_label": "Label",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.settings.pages.advanced": "Advanced",
|
||||
"repo.settings.pages.static_routes": "Static Routes",
|
||||
"repo.settings.pages.static_routes_desc": "Serve files directly from your repository at specific URL paths instead of rendering the landing page. Use exact paths (/badge.svg) or glob patterns (/schema/*).",
|
||||
"repo.settings.pages.add_route": "Add Route",
|
||||
"repo.settings.pages.redirects": "Redirects",
|
||||
"repo.settings.pages.redirects_desc": "Redirect specific paths to other URLs. The source path must start with /.",
|
||||
"repo.settings.pages.add_redirect": "Add Redirect",
|
||||
"repo.settings.pages.custom_code": "Custom Code",
|
||||
"repo.settings.pages.custom_css": "Custom CSS",
|
||||
"repo.settings.pages.custom_head": "Custom Head HTML",
|
||||
"repo.settings.pages.app_stores": "App Stores",
|
||||
"repo.settings.pages.hide_mobile_releases": "Hide Mobile Releases",
|
||||
"repo.vault": "Vault",
|
||||
"repo.vault.secrets": "Secrets",
|
||||
"repo.vault.new_secret": "New Secret",
|
||||
|
||||
@@ -3840,6 +3840,9 @@
|
||||
"repo.settings.pages.translations": "Traducciones",
|
||||
"repo.settings.pages.ai_translate": "Traducir con IA",
|
||||
"repo.settings.pages.ai_translate_success": "La traducci\u00f3n se ha generado correctamente con IA. Rev\u00edsala y ed\u00edtala seg\u00fan sea necesario.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Eliminar",
|
||||
"repo.settings.pages.save_translation": "Guardar traducci\u00f3n",
|
||||
"repo.settings.pages.translation_saved": "Traducci\u00f3n guardada correctamente.",
|
||||
@@ -3852,6 +3855,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "T\u00edtulo de la secci\u00f3n CTA",
|
||||
"repo.settings.pages.trans_cta_subheadline": "Subt\u00edtulo de la secci\u00f3n CTA",
|
||||
"repo.settings.pages.trans_cta_button": "Etiqueta del bot\u00f3n CTA",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -4021,4 +4061,4 @@
|
||||
"actions.runners.waiting_jobs": "Trabajos en espera",
|
||||
"actions.runners.back_to_runners": "Volver a Runners",
|
||||
"actions.runners.no_waiting_jobs": "No hay trabajos esperando para esta etiqueta"
|
||||
}
|
||||
}
|
||||
@@ -2935,6 +2935,9 @@
|
||||
"repo.settings.pages.translations": "ترجمهها",
|
||||
"repo.settings.pages.ai_translate": "ترجمه هوش مصنوعی",
|
||||
"repo.settings.pages.ai_translate_success": "ترجمه با موفقیت توسط هوش مصنوعی تولید شد. در صورت نیاز بررسی و ویرایش کنید.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "حذف",
|
||||
"repo.settings.pages.save_translation": "ذخیره ترجمه",
|
||||
"repo.settings.pages.translation_saved": "ترجمه با موفقیت ذخیره شد.",
|
||||
@@ -2947,6 +2950,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "عنوان بخش فراخوان",
|
||||
"repo.settings.pages.trans_cta_subheadline": "زیرعنوان بخش فراخوان",
|
||||
"repo.settings.pages.trans_cta_button": "برچسب دکمه فراخوان",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -3264,4 +3304,4 @@
|
||||
"actions.runners.waiting_jobs": "کارهای در انتظار",
|
||||
"actions.runners.back_to_runners": "بازگشت به اجراکنندهها",
|
||||
"actions.runners.no_waiting_jobs": "هیچ کاری برای این برچسب در انتظار نیست"
|
||||
}
|
||||
}
|
||||
@@ -2233,6 +2233,9 @@
|
||||
"repo.settings.pages.translations": "K\u00e4\u00e4nn\u00f6kset",
|
||||
"repo.settings.pages.ai_translate": "AI-k\u00e4\u00e4nn\u00f6s",
|
||||
"repo.settings.pages.ai_translate_success": "K\u00e4\u00e4nn\u00f6s on luotu onnistuneesti AI:n avulla. Tarkista ja muokkaa tarvittaessa.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Poista",
|
||||
"repo.settings.pages.save_translation": "Tallenna k\u00e4\u00e4nn\u00f6s",
|
||||
"repo.settings.pages.translation_saved": "K\u00e4\u00e4nn\u00f6s tallennettu onnistuneesti.",
|
||||
@@ -2245,6 +2248,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "CTA-osion otsikko",
|
||||
"repo.settings.pages.trans_cta_subheadline": "CTA-osion alaotsikko",
|
||||
"repo.settings.pages.trans_cta_button": "CTA-painikkeen teksti",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -2544,4 +2584,4 @@
|
||||
"actions.runners.waiting_jobs": "Odottavat työt",
|
||||
"actions.runners.back_to_runners": "Takaisin ajajiin",
|
||||
"actions.runners.no_waiting_jobs": "Ei odottavia töitä tälle tunnisteelle"
|
||||
}
|
||||
}
|
||||
@@ -4186,6 +4186,9 @@
|
||||
"repo.settings.pages.translations": "Traductions",
|
||||
"repo.settings.pages.ai_translate": "Traduction IA",
|
||||
"repo.settings.pages.ai_translate_success": "La traduction a \u00e9t\u00e9 g\u00e9n\u00e9r\u00e9e avec succ\u00e8s par l'IA. V\u00e9rifiez et modifiez si n\u00e9cessaire.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Supprimer",
|
||||
"repo.settings.pages.save_translation": "Enregistrer la traduction",
|
||||
"repo.settings.pages.translation_saved": "Traduction enregistr\u00e9e avec succ\u00e8s.",
|
||||
@@ -4198,6 +4201,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "Titre de la section CTA",
|
||||
"repo.settings.pages.trans_cta_subheadline": "Sous-titre de la section CTA",
|
||||
"repo.settings.pages.trans_cta_button": "Libell\u00e9 du bouton CTA",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -4367,4 +4407,4 @@
|
||||
"actions.runners.waiting_jobs": "Tâches en attente",
|
||||
"actions.runners.back_to_runners": "Retour aux runners",
|
||||
"actions.runners.no_waiting_jobs": "Aucune tâche en attente pour ce label"
|
||||
}
|
||||
}
|
||||
@@ -4115,6 +4115,9 @@
|
||||
"repo.settings.pages.translations": "Aistriúcháin",
|
||||
"repo.settings.pages.ai_translate": "Aistriúchán AI",
|
||||
"repo.settings.pages.ai_translate_success": "Ghin AI an t-aistriúchán go rathúil. Athbhreithnigh agus cuir in eagar de réir mar is gá.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Scrios",
|
||||
"repo.settings.pages.save_translation": "Sábháil aistriúchán",
|
||||
"repo.settings.pages.translation_saved": "Sábháladh an t-aistriúchán go rathúil.",
|
||||
@@ -4127,6 +4130,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "Ceannlíne rannóg CTA",
|
||||
"repo.settings.pages.trans_cta_subheadline": "Fo-cheannlíne rannóg CTA",
|
||||
"repo.settings.pages.trans_cta_button": "Lipéad cnaipe CTA",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -4413,4 +4453,4 @@
|
||||
"actions.runners.waiting_jobs": "Jabanna ag Fanacht",
|
||||
"actions.runners.back_to_runners": "Ar ais go Ritheoirí",
|
||||
"actions.runners.no_waiting_jobs": "Níl aon jabanna ag fanacht ar an lipéad seo"
|
||||
}
|
||||
}
|
||||
@@ -4021,6 +4021,9 @@
|
||||
"repo.settings.pages.translations": "\u0905\u0928\u0941\u0935\u093e\u0926",
|
||||
"repo.settings.pages.ai_translate": "AI \u0905\u0928\u0941\u0935\u093e\u0926",
|
||||
"repo.settings.pages.ai_translate_success": "AI \u0926\u094d\u0935\u093e\u0930\u093e \u0905\u0928\u0941\u0935\u093e\u0926 \u0938\u092b\u0932\u0924\u093e\u092a\u0942\u0930\u094d\u0935\u0915 \u091c\u0928\u0930\u0947\u091f \u0915\u093f\u092f\u093e \u0917\u092f\u093e \u0939\u0948\u0964 \u0906\u0935\u0936\u094d\u092f\u0915\u0924\u093e\u0928\u0941\u0938\u093e\u0930 \u0938\u092e\u0940\u0915\u094d\u0937\u093e \u0915\u0930\u0947\u0902 \u0914\u0930 \u0938\u0902\u092a\u093e\u0926\u093f\u0924 \u0915\u0930\u0947\u0902\u0964",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "\u0939\u091f\u093e\u090f\u0901",
|
||||
"repo.settings.pages.save_translation": "\u0905\u0928\u0941\u0935\u093e\u0926 \u0938\u0939\u0947\u091c\u0947\u0902",
|
||||
"repo.settings.pages.translation_saved": "\u0905\u0928\u0941\u0935\u093e\u0926 \u0938\u092b\u0932\u0924\u093e\u092a\u0942\u0930\u094d\u0935\u0915 \u0938\u0939\u0947\u091c\u093e \u0917\u092f\u093e\u0964",
|
||||
@@ -4033,6 +4036,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "CTA \u0905\u0928\u0941\u092d\u093e\u0917 \u0936\u0940\u0930\u094d\u0937\u0915",
|
||||
"repo.settings.pages.trans_cta_subheadline": "CTA \u0905\u0928\u0941\u092d\u093e\u0917 \u0909\u092a\u0936\u0940\u0930\u094d\u0937\u0915",
|
||||
"repo.settings.pages.trans_cta_button": "CTA \u092c\u091f\u0928 \u0932\u0947\u092c\u0932",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault": "वॉल्ट",
|
||||
"repo.vault.secrets": "Secrets",
|
||||
"repo.vault.new_secret": "New Secret",
|
||||
@@ -4415,4 +4455,4 @@
|
||||
"actions.runners.waiting_jobs": "प्रतीक्षारत कार्य",
|
||||
"actions.runners.back_to_runners": "रनर्स पर वापस जाएं",
|
||||
"actions.runners.no_waiting_jobs": "इस लेबल के लिए कोई कार्य प्रतीक्षा में नहीं है"
|
||||
}
|
||||
}
|
||||
@@ -2150,6 +2150,9 @@
|
||||
"repo.settings.pages.translations": "Ford\u00edt\u00e1sok",
|
||||
"repo.settings.pages.ai_translate": "AI ford\u00edt\u00e1s",
|
||||
"repo.settings.pages.ai_translate_success": "A ford\u00edt\u00e1st az AI sikeresen gener\u00e1lta. Tekintse \u00e1t \u00e9s szerkessze sz\u00fcks\u00e9g szerint.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "T\u00f6rl\u00e9s",
|
||||
"repo.settings.pages.save_translation": "Ford\u00edt\u00e1s ment\u00e9se",
|
||||
"repo.settings.pages.translation_saved": "Ford\u00edt\u00e1s sikeresen mentve.",
|
||||
@@ -2162,6 +2165,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "CTA szekci\u00f3 c\u00edmsor",
|
||||
"repo.settings.pages.trans_cta_subheadline": "CTA szekci\u00f3 alc\u00edmsor",
|
||||
"repo.settings.pages.trans_cta_button": "CTA gomb c\u00edmke",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -2461,4 +2501,4 @@
|
||||
"actions.runners.waiting_jobs": "Várakozó feladatok",
|
||||
"actions.runners.back_to_runners": "Vissza a futtatókhoz",
|
||||
"actions.runners.no_waiting_jobs": "Nincsenek várakozó feladatok ehhez a címkéhez"
|
||||
}
|
||||
}
|
||||
@@ -1974,6 +1974,9 @@
|
||||
"repo.settings.pages.translations": "Terjemahan",
|
||||
"repo.settings.pages.ai_translate": "Terjemahan AI",
|
||||
"repo.settings.pages.ai_translate_success": "Terjemahan telah berhasil dibuat oleh AI. Tinjau dan edit sesuai kebutuhan.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Hapus",
|
||||
"repo.settings.pages.save_translation": "Simpan terjemahan",
|
||||
"repo.settings.pages.translation_saved": "Terjemahan berhasil disimpan.",
|
||||
@@ -1986,6 +1989,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "Judul bagian CTA",
|
||||
"repo.settings.pages.trans_cta_subheadline": "Subjudul bagian CTA",
|
||||
"repo.settings.pages.trans_cta_button": "Label tombol CTA",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -2272,4 +2312,4 @@
|
||||
"actions.runners.waiting_jobs": "Pekerjaan Menunggu",
|
||||
"actions.runners.back_to_runners": "Kembali ke Runner",
|
||||
"actions.runners.no_waiting_jobs": "Tidak ada pekerjaan yang menunggu untuk label ini"
|
||||
}
|
||||
}
|
||||
@@ -1862,6 +1862,9 @@
|
||||
"repo.settings.pages.translations": "Þýðingar",
|
||||
"repo.settings.pages.ai_translate": "AI þýðing",
|
||||
"repo.settings.pages.ai_translate_success": "Þýðing hefur verið búin til af AI. Skoðaðu og breyttu eftir þörfum.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Eyða",
|
||||
"repo.settings.pages.save_translation": "Vista þýðingu",
|
||||
"repo.settings.pages.translation_saved": "Þýðing vistuð.",
|
||||
@@ -1874,6 +1877,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "Fyrirsögn CTA hluta",
|
||||
"repo.settings.pages.trans_cta_subheadline": "Undirfyrirsögn CTA hluta",
|
||||
"repo.settings.pages.trans_cta_button": "Merki CTA hnapps",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -2160,4 +2200,4 @@
|
||||
"actions.runners.waiting_jobs": "Bíðandi verk",
|
||||
"actions.runners.back_to_runners": "Til baka í keyrsluforrit",
|
||||
"actions.runners.no_waiting_jobs": "Engin verk bíða eftir þessu merki"
|
||||
}
|
||||
}
|
||||
@@ -3286,6 +3286,9 @@
|
||||
"repo.settings.pages.translations": "Traduzioni",
|
||||
"repo.settings.pages.ai_translate": "Traduzione IA",
|
||||
"repo.settings.pages.ai_translate_success": "La traduzione \u00e8 stata generata con successo dall'IA. Rivedi e modifica secondo necessit\u00e0.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Elimina",
|
||||
"repo.settings.pages.save_translation": "Salva traduzione",
|
||||
"repo.settings.pages.translation_saved": "Traduzione salvata con successo.",
|
||||
@@ -3298,6 +3301,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "Titolo sezione CTA",
|
||||
"repo.settings.pages.trans_cta_subheadline": "Sottotitolo sezione CTA",
|
||||
"repo.settings.pages.trans_cta_button": "Etichetta pulsante CTA",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -3467,4 +3507,4 @@
|
||||
"actions.runners.waiting_jobs": "Lavori in attesa",
|
||||
"actions.runners.back_to_runners": "Torna ai runner",
|
||||
"actions.runners.no_waiting_jobs": "Nessun lavoro in attesa per questa etichetta"
|
||||
}
|
||||
}
|
||||
@@ -4230,6 +4230,9 @@
|
||||
"repo.settings.pages.translations": "\u7ffb\u8a33",
|
||||
"repo.settings.pages.ai_translate": "AI\u7ffb\u8a33",
|
||||
"repo.settings.pages.ai_translate_success": "AI\u306b\u3088\u308b\u7ffb\u8a33\u304c\u6b63\u5e38\u306b\u751f\u6210\u3055\u308c\u307e\u3057\u305f\u3002\u5fc5\u8981\u306b\u5fdc\u3058\u3066\u78ba\u8a8d\u30fb\u7de8\u96c6\u3057\u3066\u304f\u3060\u3055\u3044\u3002",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "\u524a\u9664",
|
||||
"repo.settings.pages.save_translation": "\u7ffb\u8a33\u3092\u4fdd\u5b58",
|
||||
"repo.settings.pages.translation_saved": "\u7ffb\u8a33\u304c\u6b63\u5e38\u306b\u4fdd\u5b58\u3055\u308c\u307e\u3057\u305f\u3002",
|
||||
@@ -4242,6 +4245,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "CTA\u30bb\u30af\u30b7\u30e7\u30f3\u898b\u51fa\u3057",
|
||||
"repo.settings.pages.trans_cta_subheadline": "CTA\u30bb\u30af\u30b7\u30e7\u30f3\u30b5\u30d6\u898b\u51fa\u3057",
|
||||
"repo.settings.pages.trans_cta_button": "CTA\u30dc\u30bf\u30f3\u30e9\u30d9\u30eb",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -4411,4 +4451,4 @@
|
||||
"actions.runners.waiting_jobs": "待機中のジョブ",
|
||||
"actions.runners.back_to_runners": "ランナーに戻る",
|
||||
"actions.runners.no_waiting_jobs": "このラベルを待機しているジョブはありません"
|
||||
}
|
||||
}
|
||||
@@ -2245,6 +2245,9 @@
|
||||
"repo.settings.pages.translations": "\ubc88\uc5ed",
|
||||
"repo.settings.pages.ai_translate": "AI \ubc88\uc5ed",
|
||||
"repo.settings.pages.ai_translate_success": "AI\uac00 \ubc88\uc5ed\uc744 \uc131\uacf5\uc801\uc73c\ub85c \uc0dd\uc131\ud588\uc2b5\ub2c8\ub2e4. \ud544\uc694\uc5d0 \ub530\ub77c \ud655\uc778\ud558\uace0 \ud3b8\uc9d1\ud558\uc138\uc694.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "\uc0ad\uc81c",
|
||||
"repo.settings.pages.save_translation": "\ubc88\uc5ed \uc800\uc7a5",
|
||||
"repo.settings.pages.translation_saved": "\ubc88\uc5ed\uc774 \uc131\uacf5\uc801\uc73c\ub85c \uc800\uc7a5\ub418\uc5c8\uc2b5\ub2c8\ub2e4.",
|
||||
@@ -2257,6 +2260,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "CTA \uc139\uc158 \uc81c\ubaa9",
|
||||
"repo.settings.pages.trans_cta_subheadline": "CTA \uc139\uc158 \ubd80\uc81c\ubaa9",
|
||||
"repo.settings.pages.trans_cta_button": "CTA \ubc84\ud2bc \ub77c\ubca8",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -2426,4 +2466,4 @@
|
||||
"actions.runners.waiting_jobs": "대기 중인 작업",
|
||||
"actions.runners.back_to_runners": "러너로 돌아가기",
|
||||
"actions.runners.no_waiting_jobs": "이 레이블을 기다리는 작업이 없습니다"
|
||||
}
|
||||
}
|
||||
@@ -3756,6 +3756,9 @@
|
||||
"repo.settings.pages.translations": "Tulkojumi",
|
||||
"repo.settings.pages.ai_translate": "AI tulkojums",
|
||||
"repo.settings.pages.ai_translate_success": "Tulkojums veiksmīgi ģenerēts ar AI. Pārskatiet un rediģējiet pēc nepieciešamības.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Dzēst",
|
||||
"repo.settings.pages.save_translation": "Saglabāt tulkojumu",
|
||||
"repo.settings.pages.translation_saved": "Tulkojums veiksmīgi saglabāts.",
|
||||
@@ -3768,6 +3771,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "CTA sadaļas virsraksts",
|
||||
"repo.settings.pages.trans_cta_subheadline": "CTA sadaļas apakšvirsraksts",
|
||||
"repo.settings.pages.trans_cta_button": "CTA pogas etiķete",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -4054,4 +4094,4 @@
|
||||
"actions.runners.waiting_jobs": "Gaidošie darbi",
|
||||
"actions.runners.back_to_runners": "Atpakaļ pie izpildītājiem",
|
||||
"actions.runners.no_waiting_jobs": "Nav darbu, kas gaida šo etiķeti"
|
||||
}
|
||||
}
|
||||
@@ -2904,6 +2904,9 @@
|
||||
"repo.settings.pages.translations": "Vertalingen",
|
||||
"repo.settings.pages.ai_translate": "AI-vertaling",
|
||||
"repo.settings.pages.ai_translate_success": "Vertaling is succesvol gegenereerd door AI. Controleer en bewerk indien nodig.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Verwijderen",
|
||||
"repo.settings.pages.save_translation": "Vertaling opslaan",
|
||||
"repo.settings.pages.translation_saved": "Vertaling succesvol opgeslagen.",
|
||||
@@ -2916,6 +2919,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "CTA-sectietitel",
|
||||
"repo.settings.pages.trans_cta_subheadline": "CTA-sectie ondertitel",
|
||||
"repo.settings.pages.trans_cta_button": "CTA-knoplabel",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -3215,4 +3255,4 @@
|
||||
"actions.runners.waiting_jobs": "Wachtende taken",
|
||||
"actions.runners.back_to_runners": "Terug naar runners",
|
||||
"actions.runners.no_waiting_jobs": "Geen taken wachten op dit label"
|
||||
}
|
||||
}
|
||||
@@ -2875,6 +2875,9 @@
|
||||
"repo.settings.pages.translations": "T\u0142umaczenia",
|
||||
"repo.settings.pages.ai_translate": "T\u0142umaczenie AI",
|
||||
"repo.settings.pages.ai_translate_success": "T\u0142umaczenie zosta\u0142o wygenerowane przez AI. Sprawd\u017a i edytuj w razie potrzeby.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Usu\u0144",
|
||||
"repo.settings.pages.save_translation": "Zapisz t\u0142umaczenie",
|
||||
"repo.settings.pages.translation_saved": "T\u0142umaczenie zapisane pomy\u015blnie.",
|
||||
@@ -2887,6 +2890,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "Nag\u0142\u00f3wek sekcji CTA",
|
||||
"repo.settings.pages.trans_cta_subheadline": "Podnag\u0142\u00f3wek sekcji CTA",
|
||||
"repo.settings.pages.trans_cta_button": "Etykieta przycisku CTA",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -3186,4 +3226,4 @@
|
||||
"actions.runners.waiting_jobs": "Oczekujące zadania",
|
||||
"actions.runners.back_to_runners": "Powrót do runnerów",
|
||||
"actions.runners.no_waiting_jobs": "Brak zadań oczekujących na tę etykietę"
|
||||
}
|
||||
}
|
||||
@@ -4028,6 +4028,9 @@
|
||||
"repo.settings.pages.translations": "Tradu\u00e7\u00f5es",
|
||||
"repo.settings.pages.ai_translate": "Tradu\u00e7\u00e3o com IA",
|
||||
"repo.settings.pages.ai_translate_success": "A tradu\u00e7\u00e3o foi gerada com sucesso pela IA. Revise e edite conforme necess\u00e1rio.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Excluir",
|
||||
"repo.settings.pages.save_translation": "Salvar tradu\u00e7\u00e3o",
|
||||
"repo.settings.pages.translation_saved": "Tradu\u00e7\u00e3o salva com sucesso.",
|
||||
@@ -4040,6 +4043,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "T\u00edtulo da se\u00e7\u00e3o CTA",
|
||||
"repo.settings.pages.trans_cta_subheadline": "Subt\u00edtulo da se\u00e7\u00e3o CTA",
|
||||
"repo.settings.pages.trans_cta_button": "R\u00f3tulo do bot\u00e3o CTA",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -4208,4 +4248,4 @@
|
||||
"actions.runners.waiting_jobs": "Trabalhos em espera",
|
||||
"actions.runners.back_to_runners": "Voltar para Runners",
|
||||
"actions.runners.no_waiting_jobs": "Nenhum trabalho aguardando para este rótulo"
|
||||
}
|
||||
}
|
||||
@@ -4117,6 +4117,9 @@
|
||||
"repo.settings.pages.translations": "Tradu\u00e7\u00f5es",
|
||||
"repo.settings.pages.ai_translate": "Tradu\u00e7\u00e3o com IA",
|
||||
"repo.settings.pages.ai_translate_success": "A tradu\u00e7\u00e3o foi gerada com sucesso pela IA. Reveja e edite conforme necess\u00e1rio.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Eliminar",
|
||||
"repo.settings.pages.save_translation": "Guardar tradu\u00e7\u00e3o",
|
||||
"repo.settings.pages.translation_saved": "Tradu\u00e7\u00e3o guardada com sucesso.",
|
||||
@@ -4129,6 +4132,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "T\u00edtulo da sec\u00e7\u00e3o CTA",
|
||||
"repo.settings.pages.trans_cta_subheadline": "Subt\u00edtulo da sec\u00e7\u00e3o CTA",
|
||||
"repo.settings.pages.trans_cta_button": "Etiqueta do bot\u00e3o CTA",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -4427,4 +4467,4 @@
|
||||
"actions.runners.waiting_jobs": "Trabalhos em espera",
|
||||
"actions.runners.back_to_runners": "Voltar aos Runners",
|
||||
"actions.runners.no_waiting_jobs": "Nenhum trabalho à espera para esta etiqueta"
|
||||
}
|
||||
}
|
||||
@@ -3849,6 +3849,9 @@
|
||||
"repo.settings.pages.translations": "\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b",
|
||||
"repo.settings.pages.ai_translate": "\u0418\u0418-\u043f\u0435\u0440\u0435\u0432\u043e\u0434",
|
||||
"repo.settings.pages.ai_translate_success": "\u041f\u0435\u0440\u0435\u0432\u043e\u0434 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d \u0418\u0418. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0438 \u043e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c",
|
||||
"repo.settings.pages.save_translation": "\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434",
|
||||
"repo.settings.pages.translation_saved": "\u041f\u0435\u0440\u0435\u0432\u043e\u0434 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d.",
|
||||
@@ -3861,6 +3864,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0440\u0430\u0437\u0434\u0435\u043b\u0430 CTA",
|
||||
"repo.settings.pages.trans_cta_subheadline": "\u041f\u043e\u0434\u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0440\u0430\u0437\u0434\u0435\u043b\u0430 CTA",
|
||||
"repo.settings.pages.trans_cta_button": "\u041c\u0435\u0442\u043a\u0430 \u043a\u043d\u043e\u043f\u043a\u0438 CTA",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -4017,4 +4057,4 @@
|
||||
"actions.runners.waiting_jobs": "Ожидающие задачи",
|
||||
"actions.runners.back_to_runners": "Назад к раннерам",
|
||||
"actions.runners.no_waiting_jobs": "Нет задач, ожидающих эту метку"
|
||||
}
|
||||
}
|
||||
@@ -2921,6 +2921,9 @@
|
||||
"repo.settings.pages.translations": "පරිවර්තන",
|
||||
"repo.settings.pages.ai_translate": "AI පරිවර්තනය",
|
||||
"repo.settings.pages.ai_translate_success": "AI විසින් පරිවර්තනය සාර්ථකව උත්පාදනය කරන ලදී. අවශ්ය පරිදි සමාලෝචනය කර සංස්කරණය කරන්න.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "මකන්න",
|
||||
"repo.settings.pages.save_translation": "පරිවර්තනය සුරකින්න",
|
||||
"repo.settings.pages.translation_saved": "පරිවර්තනය සාර්ථකව සුරැකිණි.",
|
||||
@@ -2933,6 +2936,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "CTA අංශ මාතෘකාව",
|
||||
"repo.settings.pages.trans_cta_subheadline": "CTA අංශ උපමාතෘකාව",
|
||||
"repo.settings.pages.trans_cta_button": "CTA බොත්තම් ලේබලය",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -3219,4 +3259,4 @@
|
||||
"actions.runners.waiting_jobs": "බලා සිටින කාර්යයන්",
|
||||
"actions.runners.back_to_runners": "ධාවකයන් වෙත ආපසු",
|
||||
"actions.runners.no_waiting_jobs": "මෙම ලේබලය සඳහා බලා සිටින කාර්යයන් නොමැත"
|
||||
}
|
||||
}
|
||||
@@ -1947,6 +1947,9 @@
|
||||
"repo.settings.pages.translations": "Preklady",
|
||||
"repo.settings.pages.ai_translate": "AI preklad",
|
||||
"repo.settings.pages.ai_translate_success": "Preklad bol \u00faspe\u0161ne vygenerovan\u00fd AI. Skontrolujte a upravte pod\u013ea potreby.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Odstr\u00e1ni\u0165",
|
||||
"repo.settings.pages.save_translation": "Ulo\u017ei\u0165 preklad",
|
||||
"repo.settings.pages.translation_saved": "Preklad \u00faspe\u0161ne ulo\u017een\u00fd.",
|
||||
@@ -1959,6 +1962,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "Nadpis sekcie CTA",
|
||||
"repo.settings.pages.trans_cta_subheadline": "Podnadpis sekcie CTA",
|
||||
"repo.settings.pages.trans_cta_button": "\u0160t\u00edtok tla\u010didla CTA",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -2245,4 +2285,4 @@
|
||||
"actions.runners.waiting_jobs": "Čakajúce úlohy",
|
||||
"actions.runners.back_to_runners": "Späť na bežcov",
|
||||
"actions.runners.no_waiting_jobs": "Žiadne úlohy nečakajú na tento štítok"
|
||||
}
|
||||
}
|
||||
@@ -2505,6 +2505,9 @@
|
||||
"repo.settings.pages.translations": "\u00d6vers\u00e4ttningar",
|
||||
"repo.settings.pages.ai_translate": "AI-\u00f6vers\u00e4ttning",
|
||||
"repo.settings.pages.ai_translate_success": "\u00d6vers\u00e4ttningen har genererats av AI. Granska och redigera vid behov.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Ta bort",
|
||||
"repo.settings.pages.save_translation": "Spara \u00f6vers\u00e4ttning",
|
||||
"repo.settings.pages.translation_saved": "\u00d6vers\u00e4ttningen har sparats.",
|
||||
@@ -2517,6 +2520,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "CTA-sektionsrubrik",
|
||||
"repo.settings.pages.trans_cta_subheadline": "CTA-sektions underrubrik",
|
||||
"repo.settings.pages.trans_cta_button": "CTA-knappsetikett",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -2816,4 +2856,4 @@
|
||||
"actions.runners.waiting_jobs": "Väntande jobb",
|
||||
"actions.runners.back_to_runners": "Tillbaka till runners",
|
||||
"actions.runners.no_waiting_jobs": "Inga jobb väntar på denna etikett"
|
||||
}
|
||||
}
|
||||
@@ -4114,6 +4114,9 @@
|
||||
"repo.settings.pages.translations": "\u00c7eviriler",
|
||||
"repo.settings.pages.ai_translate": "AI \u00c7eviri",
|
||||
"repo.settings.pages.ai_translate_success": "\u00c7eviri AI taraf\u0131ndan ba\u015far\u0131yla olu\u015fturuldu. Gerekti\u011fi gibi inceleyin ve d\u00fczenleyin.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "Sil",
|
||||
"repo.settings.pages.save_translation": "\u00c7eviriyi kaydet",
|
||||
"repo.settings.pages.translation_saved": "\u00c7eviri ba\u015far\u0131yla kaydedildi.",
|
||||
@@ -4126,6 +4129,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "CTA b\u00f6l\u00fcm ba\u015fl\u0131\u011f\u0131",
|
||||
"repo.settings.pages.trans_cta_subheadline": "CTA b\u00f6l\u00fcm alt ba\u015fl\u0131\u011f\u0131",
|
||||
"repo.settings.pages.trans_cta_button": "CTA d\u00fc\u011fme etiketi",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -4425,4 +4465,4 @@
|
||||
"actions.runners.waiting_jobs": "Bekleyen İşler",
|
||||
"actions.runners.back_to_runners": "Çalıştırıcılara Dön",
|
||||
"actions.runners.no_waiting_jobs": "Bu etiket için bekleyen iş yok"
|
||||
}
|
||||
}
|
||||
@@ -3899,6 +3899,9 @@
|
||||
"repo.settings.pages.translations": "\u041f\u0435\u0440\u0435\u043a\u043b\u0430\u0434\u0438",
|
||||
"repo.settings.pages.ai_translate": "\u0428\u0406-\u043f\u0435\u0440\u0435\u043a\u043b\u0430\u0434",
|
||||
"repo.settings.pages.ai_translate_success": "\u041f\u0435\u0440\u0435\u043a\u043b\u0430\u0434 \u0443\u0441\u043f\u0456\u0448\u043d\u043e \u0437\u0433\u0435\u043d\u0435\u0440\u043e\u0432\u0430\u043d\u043e \u0428\u0406. \u041f\u0435\u0440\u0435\u0432\u0456\u0440\u0442\u0435 \u0442\u0430 \u0432\u0456\u0434\u0440\u0435\u0434\u0430\u0433\u0443\u0439\u0442\u0435 \u0437\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0438.",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "\u0412\u0438\u0434\u0430\u043b\u0438\u0442\u0438",
|
||||
"repo.settings.pages.save_translation": "\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u0430\u0434",
|
||||
"repo.settings.pages.translation_saved": "\u041f\u0435\u0440\u0435\u043a\u043b\u0430\u0434 \u0443\u0441\u043f\u0456\u0448\u043d\u043e \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043d\u043e.",
|
||||
@@ -3911,6 +3914,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0440\u043e\u0437\u0434\u0456\u043b\u0443 CTA",
|
||||
"repo.settings.pages.trans_cta_subheadline": "\u041f\u0456\u0434\u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0440\u043e\u0437\u0434\u0456\u043b\u0443 CTA",
|
||||
"repo.settings.pages.trans_cta_button": "\u041c\u0456\u0442\u043a\u0430 \u043a\u043d\u043e\u043f\u043a\u0438 CTA",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -4197,4 +4237,4 @@
|
||||
"actions.runners.waiting_jobs": "Очікуючі завдання",
|
||||
"actions.runners.back_to_runners": "Назад до раннерів",
|
||||
"actions.runners.no_waiting_jobs": "Немає завдань, що очікують цю мітку"
|
||||
}
|
||||
}
|
||||
@@ -4248,6 +4248,9 @@
|
||||
"repo.settings.pages.translations": "\u7ffb\u8bd1",
|
||||
"repo.settings.pages.ai_translate": "AI\u7ffb\u8bd1",
|
||||
"repo.settings.pages.ai_translate_success": "AI\u5df2\u6210\u529f\u751f\u6210\u7ffb\u8bd1\u3002\u8bf7\u6839\u636e\u9700\u8981\u67e5\u770b\u548c\u7f16\u8f91\u3002",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "\u5220\u9664",
|
||||
"repo.settings.pages.save_translation": "\u4fdd\u5b58\u7ffb\u8bd1",
|
||||
"repo.settings.pages.translation_saved": "\u7ffb\u8bd1\u5df2\u6210\u529f\u4fdd\u5b58\u3002",
|
||||
@@ -4260,6 +4263,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "\u884c\u52a8\u53f7\u53ec\u533a\u57df\u6807\u9898",
|
||||
"repo.settings.pages.trans_cta_subheadline": "\u884c\u52a8\u53f7\u53ec\u533a\u57df\u526f\u6807\u9898",
|
||||
"repo.settings.pages.trans_cta_button": "\u884c\u52a8\u53f7\u53ec\u6309\u94ae\u6807\u7b7e",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -4429,4 +4469,4 @@
|
||||
"actions.runners.waiting_jobs": "等待中的任务",
|
||||
"actions.runners.back_to_runners": "返回运行器",
|
||||
"actions.runners.no_waiting_jobs": "没有等待此标签的任务"
|
||||
}
|
||||
}
|
||||
@@ -4156,6 +4156,9 @@
|
||||
"repo.settings.pages.translations": "\u7ffb\u8b6f",
|
||||
"repo.settings.pages.ai_translate": "AI\u7ffb\u8b6f",
|
||||
"repo.settings.pages.ai_translate_success": "AI\u5df2\u6210\u529f\u7522\u751f\u7ffb\u8b6f\u3002\u8acb\u6839\u64da\u9700\u8981\u6aa2\u8996\u548c\u7de8\u8f2f\u3002",
|
||||
"repo.settings.pages.ai_translate_all": "Translate All (AI)",
|
||||
"repo.settings.pages.ai_translate_all_success": "Successfully translated %d languages.",
|
||||
"repo.settings.pages.ai_translate_all_partial": "Translated %d of %d languages. %d failed.",
|
||||
"repo.settings.pages.delete_translation": "\u522a\u9664",
|
||||
"repo.settings.pages.save_translation": "\u5132\u5b58\u7ffb\u8b6f",
|
||||
"repo.settings.pages.translation_saved": "\u7ffb\u8b6f\u5df2\u6210\u529f\u5132\u5b58\u3002",
|
||||
@@ -4168,6 +4171,43 @@
|
||||
"repo.settings.pages.trans_cta_headline": "\u884c\u52d5\u547c\u7c72\u5340\u57df\u6a19\u984c",
|
||||
"repo.settings.pages.trans_cta_subheadline": "\u884c\u52d5\u547c\u7c72\u5340\u57df\u526f\u6a19\u984c",
|
||||
"repo.settings.pages.trans_cta_button": "\u884c\u52d5\u547c\u7c72\u6309\u9215\u6a19\u7c64",
|
||||
"repo.settings.pages.trans_section_brand": "Brand",
|
||||
"repo.settings.pages.trans_section_hero": "Hero",
|
||||
"repo.settings.pages.trans_section_stats": "Stats",
|
||||
"repo.settings.pages.trans_section_value_props": "Value Propositions",
|
||||
"repo.settings.pages.trans_section_features": "Features",
|
||||
"repo.settings.pages.trans_section_testimonials": "Testimonials",
|
||||
"repo.settings.pages.trans_section_pricing": "Pricing",
|
||||
"repo.settings.pages.trans_section_cta": "Call to Action",
|
||||
"repo.settings.pages.trans_section_blog": "Blog",
|
||||
"repo.settings.pages.trans_section_gallery": "Gallery",
|
||||
"repo.settings.pages.trans_section_comparison": "Comparison",
|
||||
"repo.settings.pages.trans_section_footer": "Footer",
|
||||
"repo.settings.pages.trans_section_seo": "SEO",
|
||||
"repo.settings.pages.trans_brand_name": "Brand Name",
|
||||
"repo.settings.pages.trans_brand_tagline": "Tagline",
|
||||
"repo.settings.pages.trans_stat_value": "Value",
|
||||
"repo.settings.pages.trans_stat_label": "Label",
|
||||
"repo.settings.pages.trans_title": "Title",
|
||||
"repo.settings.pages.trans_description": "Description",
|
||||
"repo.settings.pages.trans_quote": "Quote",
|
||||
"repo.settings.pages.trans_role": "Role",
|
||||
"repo.settings.pages.trans_pricing_headline": "Pricing Headline",
|
||||
"repo.settings.pages.trans_pricing_subheadline": "Pricing Subheadline",
|
||||
"repo.settings.pages.trans_plan_name": "Plan Name",
|
||||
"repo.settings.pages.trans_plan_period": "Period",
|
||||
"repo.settings.pages.trans_plan_cta": "Plan Button",
|
||||
"repo.settings.pages.trans_blog_headline": "Blog Headline",
|
||||
"repo.settings.pages.trans_blog_subheadline": "Blog Subheadline",
|
||||
"repo.settings.pages.trans_blog_cta": "Blog Button",
|
||||
"repo.settings.pages.trans_gallery_headline": "Gallery Headline",
|
||||
"repo.settings.pages.trans_gallery_subheadline": "Gallery Subheadline",
|
||||
"repo.settings.pages.trans_comparison_headline": "Comparison Headline",
|
||||
"repo.settings.pages.trans_comparison_subheadline": "Comparison Subheadline",
|
||||
"repo.settings.pages.trans_footer_copyright": "Copyright",
|
||||
"repo.settings.pages.trans_footer_link": "Link Label",
|
||||
"repo.settings.pages.trans_seo_title": "SEO Title",
|
||||
"repo.settings.pages.trans_seo_description": "SEO Description",
|
||||
"repo.vault.plugin_not_installed": "Vault Plugin Not Installed",
|
||||
"repo.vault.plugin_not_installed_desc": "The Vault plugin is not installed on this server. Contact your administrator to enable secrets management.",
|
||||
"repo.vault.secret_limit_reached": "Secret limit reached. Your current tier allows %d secrets per repository. Upgrade to Pro for unlimited secrets.",
|
||||
@@ -4337,4 +4377,4 @@
|
||||
"actions.runners.waiting_jobs": "等待中的工作",
|
||||
"actions.runners.back_to_runners": "返回執行器",
|
||||
"actions.runners.no_waiting_jobs": "沒有等待此標籤的工作"
|
||||
}
|
||||
}
|
||||
@@ -171,10 +171,25 @@ func Routes() *web.Router {
|
||||
// Upload instructions endpoint
|
||||
m.Get("/upload/instructions", GetUploadInstructions)
|
||||
|
||||
// Public landing page API - for private repos with public_landing enabled
|
||||
// Landing page API
|
||||
m.Group("/repos/{owner}/{repo}/pages", func() {
|
||||
// Public read endpoints
|
||||
m.Get("/config", repoAssignmentWithPublicAccess(), GetPagesConfig)
|
||||
m.Get("/content", repoAssignmentWithPublicAccess(), GetPagesContent)
|
||||
|
||||
// Write endpoints (require auth + repo admin)
|
||||
m.Group("/config", func() {
|
||||
m.Put("", web.Bind(api.UpdatePagesConfigOption{}), UpdatePagesConfig)
|
||||
m.Patch("", web.Bind(api.UpdatePagesConfigOption{}), PatchPagesConfig)
|
||||
m.Put("/brand", web.Bind(api.UpdatePagesBrandOption{}), UpdatePagesBrand)
|
||||
m.Put("/hero", web.Bind(api.UpdatePagesHeroOption{}), UpdatePagesHero)
|
||||
m.Put("/content", web.Bind(api.UpdatePagesContentOption{}), UpdatePagesContentSection)
|
||||
m.Put("/comparison", web.Bind(api.UpdatePagesComparisonOption{}), UpdatePagesComparison)
|
||||
m.Put("/social", web.Bind(api.UpdatePagesSocialOption{}), UpdatePagesSocial)
|
||||
m.Put("/pricing", web.Bind(api.UpdatePagesPricingOption{}), UpdatePagesPricing)
|
||||
m.Put("/footer", web.Bind(api.UpdatePagesFooterOption{}), UpdatePagesFooter)
|
||||
m.Put("/theme", web.Bind(api.UpdatePagesThemeOption{}), UpdatePagesTheme)
|
||||
}, repoAssignment(), reqToken())
|
||||
})
|
||||
|
||||
// Blog v2 API - repository blog endpoints
|
||||
|
||||
@@ -685,9 +685,10 @@ func handleInitialize(ctx *context_service.APIContext, req *MCPRequest) {
|
||||
}
|
||||
|
||||
func handleToolsList(ctx *context_service.APIContext, req *MCPRequest) {
|
||||
allTools := make([]MCPTool, 0, len(mcpTools)+len(mcpAITools))
|
||||
allTools := make([]MCPTool, 0, len(mcpTools)+len(mcpAITools)+len(mcpPagesTools))
|
||||
allTools = append(allTools, mcpTools...)
|
||||
allTools = append(allTools, mcpAITools...)
|
||||
allTools = append(allTools, mcpPagesTools...)
|
||||
result := MCPToolsListResult{Tools: allTools}
|
||||
sendMCPResult(ctx, req.ID, result)
|
||||
}
|
||||
@@ -759,6 +760,35 @@ func handleToolsCall(ctx *context_service.APIContext, req *MCPRequest) {
|
||||
result, err = toolListIssues(ctx, params.Arguments)
|
||||
case "get_issue":
|
||||
result, err = toolGetIssue(ctx, params.Arguments)
|
||||
// Landing Pages tools
|
||||
case "get_landing_config":
|
||||
result, err = toolGetLandingConfig(ctx, params.Arguments)
|
||||
case "list_landing_templates":
|
||||
result, err = toolListLandingTemplates(ctx, params.Arguments)
|
||||
case "enable_landing_page":
|
||||
result, err = toolEnableLandingPage(ctx, params.Arguments)
|
||||
case "update_landing_brand":
|
||||
result, err = toolUpdateLandingBrand(ctx, params.Arguments)
|
||||
case "update_landing_hero":
|
||||
result, err = toolUpdateLandingHero(ctx, params.Arguments)
|
||||
case "update_landing_pricing":
|
||||
result, err = toolUpdateLandingPricing(ctx, params.Arguments)
|
||||
case "update_landing_comparison":
|
||||
result, err = toolUpdateLandingComparison(ctx, params.Arguments)
|
||||
case "update_landing_features":
|
||||
result, err = toolUpdateLandingFeatures(ctx, params.Arguments)
|
||||
case "update_landing_social_proof":
|
||||
result, err = toolUpdateLandingSocialProof(ctx, params.Arguments)
|
||||
case "update_landing_seo":
|
||||
result, err = toolUpdateLandingSEO(ctx, params.Arguments)
|
||||
case "update_landing_theme":
|
||||
result, err = toolUpdateLandingTheme(ctx, params.Arguments)
|
||||
case "update_landing_stats":
|
||||
result, err = toolUpdateLandingStats(ctx, params.Arguments)
|
||||
case "update_landing_value_props":
|
||||
result, err = toolUpdateLandingValueProps(ctx, params.Arguments)
|
||||
case "update_landing_cta":
|
||||
result, err = toolUpdateLandingCTA(ctx, params.Arguments)
|
||||
default:
|
||||
sendMCPError(ctx, req.ID, -32602, "Unknown tool", params.Name)
|
||||
return
|
||||
|
||||
794
routers/api/v2/mcp_pages.go
Normal file
794
routers/api/v2/mcp_pages.go
Normal file
@@ -0,0 +1,794 @@
|
||||
// Copyright 2026 MarketAlly. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
repo_model "code.gitcaddy.com/server/v3/models/repo"
|
||||
user_model "code.gitcaddy.com/server/v3/models/user"
|
||||
"code.gitcaddy.com/server/v3/modules/json"
|
||||
pages_module "code.gitcaddy.com/server/v3/modules/pages"
|
||||
"code.gitcaddy.com/server/v3/services/context"
|
||||
pages_service "code.gitcaddy.com/server/v3/services/pages"
|
||||
)
|
||||
|
||||
// Landing Pages MCP Tools
|
||||
var mcpPagesTools = []MCPTool{
|
||||
{
|
||||
Name: "get_landing_config",
|
||||
Description: "Get the full landing page configuration for a repository. Returns all sections: brand, hero, pricing, comparison, features, social proof, SEO, and more.",
|
||||
InputSchema: map[string]any{
|
||||
"type": "object",
|
||||
"required": []string{"owner", "repo"},
|
||||
"properties": map[string]any{
|
||||
"owner": map[string]any{"type": "string", "description": "Repository owner"},
|
||||
"repo": map[string]any{"type": "string", "description": "Repository name"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "list_landing_templates",
|
||||
Description: "List available landing page templates with display names. Templates include: open-source-hero, saas-conversion, bold-marketing, developer-tool, and more.",
|
||||
InputSchema: map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "enable_landing_page",
|
||||
Description: "Enable or disable the landing page for a repository. Optionally set a template.",
|
||||
InputSchema: map[string]any{
|
||||
"type": "object",
|
||||
"required": []string{"owner", "repo", "enabled"},
|
||||
"properties": map[string]any{
|
||||
"owner": map[string]any{"type": "string", "description": "Repository owner"},
|
||||
"repo": map[string]any{"type": "string", "description": "Repository name"},
|
||||
"enabled": map[string]any{"type": "boolean", "description": "Enable or disable"},
|
||||
"template": map[string]any{"type": "string", "description": "Template name (e.g., 'saas-conversion', 'open-source-hero')"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "update_landing_brand",
|
||||
Description: "Update the brand section of a landing page: name, logo URL, tagline, favicon.",
|
||||
InputSchema: map[string]any{
|
||||
"type": "object",
|
||||
"required": []string{"owner", "repo"},
|
||||
"properties": map[string]any{
|
||||
"owner": map[string]any{"type": "string", "description": "Repository owner"},
|
||||
"repo": map[string]any{"type": "string", "description": "Repository name"},
|
||||
"name": map[string]any{"type": "string", "description": "Brand name"},
|
||||
"logo_url": map[string]any{"type": "string", "description": "Logo image URL"},
|
||||
"tagline": map[string]any{"type": "string", "description": "Brand tagline"},
|
||||
"favicon_url": map[string]any{"type": "string", "description": "Favicon URL"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "update_landing_hero",
|
||||
Description: "Update the hero section: headline, subheadline, CTA buttons, hero image or video URL.",
|
||||
InputSchema: map[string]any{
|
||||
"type": "object",
|
||||
"required": []string{"owner", "repo"},
|
||||
"properties": map[string]any{
|
||||
"owner": map[string]any{"type": "string", "description": "Repository owner"},
|
||||
"repo": map[string]any{"type": "string", "description": "Repository name"},
|
||||
"headline": map[string]any{"type": "string", "description": "Main headline"},
|
||||
"subheadline": map[string]any{"type": "string", "description": "Supporting text"},
|
||||
"image_url": map[string]any{"type": "string", "description": "Hero image URL"},
|
||||
"video_url": map[string]any{"type": "string", "description": "Hero video URL"},
|
||||
"primary_cta_label": map[string]any{"type": "string", "description": "Primary CTA button label"},
|
||||
"primary_cta_url": map[string]any{"type": "string", "description": "Primary CTA button URL"},
|
||||
"secondary_cta_label": map[string]any{"type": "string", "description": "Secondary CTA button label"},
|
||||
"secondary_cta_url": map[string]any{"type": "string", "description": "Secondary CTA button URL"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "update_landing_pricing",
|
||||
Description: "Update the pricing section with plans. Each plan has a name, price, period, feature list, CTA, and optional 'featured' flag.",
|
||||
InputSchema: map[string]any{
|
||||
"type": "object",
|
||||
"required": []string{"owner", "repo", "plans"},
|
||||
"properties": map[string]any{
|
||||
"owner": map[string]any{"type": "string", "description": "Repository owner"},
|
||||
"repo": map[string]any{"type": "string", "description": "Repository name"},
|
||||
"headline": map[string]any{"type": "string", "description": "Pricing section headline"},
|
||||
"subheadline": map[string]any{"type": "string", "description": "Pricing section subheadline"},
|
||||
"plans": map[string]any{
|
||||
"type": "array",
|
||||
"items": map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"name": map[string]any{"type": "string"},
|
||||
"price": map[string]any{"type": "string"},
|
||||
"period": map[string]any{"type": "string"},
|
||||
"features": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
|
||||
"cta": map[string]any{"type": "string"},
|
||||
"featured": map[string]any{"type": "boolean"},
|
||||
},
|
||||
},
|
||||
"description": "Array of pricing plans",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "update_landing_comparison",
|
||||
Description: "Update the feature comparison matrix. Define columns (products/tiers) and groups of features with per-column values.",
|
||||
InputSchema: map[string]any{
|
||||
"type": "object",
|
||||
"required": []string{"owner", "repo"},
|
||||
"properties": map[string]any{
|
||||
"owner": map[string]any{"type": "string", "description": "Repository owner"},
|
||||
"repo": map[string]any{"type": "string", "description": "Repository name"},
|
||||
"enabled": map[string]any{"type": "boolean", "description": "Enable comparison section"},
|
||||
"headline": map[string]any{"type": "string", "description": "Section headline"},
|
||||
"subheadline": map[string]any{"type": "string", "description": "Section subheadline"},
|
||||
"columns": map[string]any{
|
||||
"type": "array",
|
||||
"items": map[string]any{"type": "string"},
|
||||
"description": "Column headers (e.g., ['Free', 'Pro', 'Enterprise'])",
|
||||
},
|
||||
"groups": map[string]any{
|
||||
"type": "array",
|
||||
"items": map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"name": map[string]any{"type": "string"},
|
||||
"features": map[string]any{
|
||||
"type": "array",
|
||||
"items": map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"name": map[string]any{"type": "string"},
|
||||
"values": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"description": "Feature groups with per-column values",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "update_landing_features",
|
||||
Description: "Update the features section with title, description, and optional icon/image for each feature.",
|
||||
InputSchema: map[string]any{
|
||||
"type": "object",
|
||||
"required": []string{"owner", "repo", "features"},
|
||||
"properties": map[string]any{
|
||||
"owner": map[string]any{"type": "string", "description": "Repository owner"},
|
||||
"repo": map[string]any{"type": "string", "description": "Repository name"},
|
||||
"features": map[string]any{
|
||||
"type": "array",
|
||||
"items": map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"title": map[string]any{"type": "string"},
|
||||
"description": map[string]any{"type": "string"},
|
||||
"icon": map[string]any{"type": "string"},
|
||||
"image_url": map[string]any{"type": "string"},
|
||||
},
|
||||
},
|
||||
"description": "Array of features",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "update_landing_social_proof",
|
||||
Description: "Update testimonials and client logos for social proof.",
|
||||
InputSchema: map[string]any{
|
||||
"type": "object",
|
||||
"required": []string{"owner", "repo"},
|
||||
"properties": map[string]any{
|
||||
"owner": map[string]any{"type": "string", "description": "Repository owner"},
|
||||
"repo": map[string]any{"type": "string", "description": "Repository name"},
|
||||
"logos": map[string]any{
|
||||
"type": "array",
|
||||
"items": map[string]any{"type": "string"},
|
||||
"description": "Client/partner logo URLs",
|
||||
},
|
||||
"testimonials": map[string]any{
|
||||
"type": "array",
|
||||
"items": map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"quote": map[string]any{"type": "string"},
|
||||
"author": map[string]any{"type": "string"},
|
||||
"role": map[string]any{"type": "string"},
|
||||
"avatar": map[string]any{"type": "string"},
|
||||
},
|
||||
},
|
||||
"description": "Testimonials",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "update_landing_seo",
|
||||
Description: "Update SEO metadata: title, description, keywords, Open Graph image, Twitter card settings.",
|
||||
InputSchema: map[string]any{
|
||||
"type": "object",
|
||||
"required": []string{"owner", "repo"},
|
||||
"properties": map[string]any{
|
||||
"owner": map[string]any{"type": "string", "description": "Repository owner"},
|
||||
"repo": map[string]any{"type": "string", "description": "Repository name"},
|
||||
"title": map[string]any{"type": "string", "description": "SEO title"},
|
||||
"description": map[string]any{"type": "string", "description": "SEO description"},
|
||||
"keywords": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "description": "SEO keywords"},
|
||||
"og_image": map[string]any{"type": "string", "description": "Open Graph image URL"},
|
||||
"twitter_card": map[string]any{"type": "string", "description": "Twitter card type (summary, summary_large_image)"},
|
||||
"twitter_site": map[string]any{"type": "string", "description": "Twitter @handle"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "update_landing_theme",
|
||||
Description: "Update the visual theme: primary color, accent color, light/dark/auto mode.",
|
||||
InputSchema: map[string]any{
|
||||
"type": "object",
|
||||
"required": []string{"owner", "repo"},
|
||||
"properties": map[string]any{
|
||||
"owner": map[string]any{"type": "string", "description": "Repository owner"},
|
||||
"repo": map[string]any{"type": "string", "description": "Repository name"},
|
||||
"primary_color": map[string]any{"type": "string", "description": "Primary brand color (hex, e.g., '#512BD4')"},
|
||||
"accent_color": map[string]any{"type": "string", "description": "Accent color (hex)"},
|
||||
"mode": map[string]any{"type": "string", "enum": []string{"light", "dark", "auto"}, "description": "Color mode"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "update_landing_stats",
|
||||
Description: "Update the stats counters displayed on the landing page. Each stat has a value and label.",
|
||||
InputSchema: map[string]any{
|
||||
"type": "object",
|
||||
"required": []string{"owner", "repo", "stats"},
|
||||
"properties": map[string]any{
|
||||
"owner": map[string]any{"type": "string", "description": "Repository owner"},
|
||||
"repo": map[string]any{"type": "string", "description": "Repository name"},
|
||||
"stats": map[string]any{
|
||||
"type": "array",
|
||||
"items": map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"value": map[string]any{"type": "string"},
|
||||
"label": map[string]any{"type": "string"},
|
||||
},
|
||||
},
|
||||
"description": "Array of stat counters (e.g., [{value: '15+', label: 'Tools'}])",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "update_landing_value_props",
|
||||
Description: "Update the value propositions section. Each value prop has a title, description, and icon.",
|
||||
InputSchema: map[string]any{
|
||||
"type": "object",
|
||||
"required": []string{"owner", "repo", "value_props"},
|
||||
"properties": map[string]any{
|
||||
"owner": map[string]any{"type": "string", "description": "Repository owner"},
|
||||
"repo": map[string]any{"type": "string", "description": "Repository name"},
|
||||
"value_props": map[string]any{
|
||||
"type": "array",
|
||||
"items": map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"title": map[string]any{"type": "string"},
|
||||
"description": map[string]any{"type": "string"},
|
||||
"icon": map[string]any{"type": "string"},
|
||||
},
|
||||
},
|
||||
"description": "Array of value propositions",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "update_landing_cta",
|
||||
Description: "Update the call-to-action section at the bottom of the page with headline, subheadline, and button.",
|
||||
InputSchema: map[string]any{
|
||||
"type": "object",
|
||||
"required": []string{"owner", "repo"},
|
||||
"properties": map[string]any{
|
||||
"owner": map[string]any{"type": "string", "description": "Repository owner"},
|
||||
"repo": map[string]any{"type": "string", "description": "Repository name"},
|
||||
"headline": map[string]any{"type": "string", "description": "CTA headline"},
|
||||
"subheadline": map[string]any{"type": "string", "description": "CTA subheadline"},
|
||||
"button_label": map[string]any{"type": "string", "description": "Button text"},
|
||||
"button_url": map[string]any{"type": "string", "description": "Button URL"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// ── Tool Implementations ──────────────────────────────────
|
||||
|
||||
func toolGetLandingConfig(ctx *context.APIContext, args map[string]any) (any, error) {
|
||||
owner, repo, err := resolveOwnerRepo(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repoObj, err := getRepoByOwnerAndName(ctx, owner, repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config, err := pages_service.GetPagesConfig(ctx, repoObj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get pages config: %w", err)
|
||||
}
|
||||
if config == nil {
|
||||
return map[string]any{"enabled": false, "message": "No landing page configured"}, nil
|
||||
}
|
||||
|
||||
return buildFullResponse(config), nil
|
||||
}
|
||||
|
||||
func toolListLandingTemplates(_ *context.APIContext, _ map[string]any) (any, error) { //nolint:unparam // signature must match tool handler type
|
||||
templates := pages_module.ValidTemplates()
|
||||
displayNames := pages_module.TemplateDisplayNames()
|
||||
|
||||
result := make([]map[string]string, 0, len(templates))
|
||||
for _, t := range templates {
|
||||
result = append(result, map[string]string{
|
||||
"id": t,
|
||||
"name": displayNames[t],
|
||||
})
|
||||
}
|
||||
return map[string]any{"templates": result}, nil
|
||||
}
|
||||
|
||||
func toolEnableLandingPage(ctx *context.APIContext, args map[string]any) (any, error) {
|
||||
owner, repo, err := resolveOwnerRepo(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repoObj, err := getRepoByOwnerAndName(ctx, owner, repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
enabled, _ := args["enabled"].(bool)
|
||||
template, _ := args["template"].(string)
|
||||
|
||||
if enabled {
|
||||
if template == "" {
|
||||
template = "open-source-hero"
|
||||
}
|
||||
if !pages_module.IsValidTemplate(template) {
|
||||
return nil, fmt.Errorf("invalid template: %s", template)
|
||||
}
|
||||
if err := pages_service.EnablePages(ctx, repoObj, template); err != nil {
|
||||
return nil, fmt.Errorf("enable pages: %w", err)
|
||||
}
|
||||
return map[string]any{"enabled": true, "template": template}, nil
|
||||
}
|
||||
|
||||
if err := pages_service.DisablePages(ctx, repoObj); err != nil {
|
||||
return nil, fmt.Errorf("disable pages: %w", err)
|
||||
}
|
||||
return map[string]any{"enabled": false}, nil
|
||||
}
|
||||
|
||||
func toolUpdateLandingBrand(ctx *context.APIContext, args map[string]any) (any, error) {
|
||||
config, repoObj, err := getConfigForUpdate(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if v, ok := args["name"].(string); ok {
|
||||
config.Brand.Name = v
|
||||
}
|
||||
if v, ok := args["logo_url"].(string); ok {
|
||||
config.Brand.LogoURL = v
|
||||
}
|
||||
if v, ok := args["tagline"].(string); ok {
|
||||
config.Brand.Tagline = v
|
||||
}
|
||||
if v, ok := args["favicon_url"].(string); ok {
|
||||
config.Brand.FaviconURL = v
|
||||
}
|
||||
|
||||
return saveAndReturn(ctx, repoObj, config, "brand")
|
||||
}
|
||||
|
||||
func toolUpdateLandingHero(ctx *context.APIContext, args map[string]any) (any, error) {
|
||||
config, repoObj, err := getConfigForUpdate(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if v, ok := args["headline"].(string); ok {
|
||||
config.Hero.Headline = v
|
||||
}
|
||||
if v, ok := args["subheadline"].(string); ok {
|
||||
config.Hero.Subheadline = v
|
||||
}
|
||||
if v, ok := args["image_url"].(string); ok {
|
||||
config.Hero.ImageURL = v
|
||||
}
|
||||
if v, ok := args["video_url"].(string); ok {
|
||||
config.Hero.VideoURL = v
|
||||
}
|
||||
if v, ok := args["primary_cta_label"].(string); ok {
|
||||
config.Hero.PrimaryCTA.Label = v
|
||||
}
|
||||
if v, ok := args["primary_cta_url"].(string); ok {
|
||||
config.Hero.PrimaryCTA.URL = v
|
||||
}
|
||||
if v, ok := args["secondary_cta_label"].(string); ok {
|
||||
config.Hero.SecondaryCTA.Label = v
|
||||
}
|
||||
if v, ok := args["secondary_cta_url"].(string); ok {
|
||||
config.Hero.SecondaryCTA.URL = v
|
||||
}
|
||||
|
||||
return saveAndReturn(ctx, repoObj, config, "hero")
|
||||
}
|
||||
|
||||
func toolUpdateLandingPricing(ctx *context.APIContext, args map[string]any) (any, error) {
|
||||
config, repoObj, err := getConfigForUpdate(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if v, ok := args["headline"].(string); ok {
|
||||
config.Pricing.Headline = v
|
||||
}
|
||||
if v, ok := args["subheadline"].(string); ok {
|
||||
config.Pricing.Subheadline = v
|
||||
}
|
||||
if plans, ok := args["plans"].([]any); ok {
|
||||
config.Pricing.Plans = nil
|
||||
for _, p := range plans {
|
||||
if pm, ok := p.(map[string]any); ok {
|
||||
plan := pages_module.PricingPlanConfig{
|
||||
Name: strVal(pm, "name"),
|
||||
Price: strVal(pm, "price"),
|
||||
Period: strVal(pm, "period"),
|
||||
CTA: strVal(pm, "cta"),
|
||||
Featured: boolVal(pm, "featured"),
|
||||
}
|
||||
if features, ok := pm["features"].([]any); ok {
|
||||
for _, f := range features {
|
||||
if s, ok := f.(string); ok {
|
||||
plan.Features = append(plan.Features, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
config.Pricing.Plans = append(config.Pricing.Plans, plan)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return saveAndReturn(ctx, repoObj, config, "pricing")
|
||||
}
|
||||
|
||||
func toolUpdateLandingComparison(ctx *context.APIContext, args map[string]any) (any, error) {
|
||||
config, repoObj, err := getConfigForUpdate(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if v, ok := args["enabled"].(bool); ok {
|
||||
config.Comparison.Enabled = v
|
||||
}
|
||||
if v, ok := args["headline"].(string); ok {
|
||||
config.Comparison.Headline = v
|
||||
}
|
||||
if v, ok := args["subheadline"].(string); ok {
|
||||
config.Comparison.Subheadline = v
|
||||
}
|
||||
if cols, ok := args["columns"].([]any); ok {
|
||||
config.Comparison.Columns = nil
|
||||
for _, c := range cols {
|
||||
if s, ok := c.(string); ok {
|
||||
config.Comparison.Columns = append(config.Comparison.Columns, pages_module.ComparisonColumnConfig{Name: s})
|
||||
} else if cm, ok := c.(map[string]any); ok {
|
||||
config.Comparison.Columns = append(config.Comparison.Columns, pages_module.ComparisonColumnConfig{
|
||||
Name: strVal(cm, "name"),
|
||||
Highlight: boolVal(cm, "highlight"),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
if groups, ok := args["groups"].([]any); ok {
|
||||
config.Comparison.Groups = nil
|
||||
for _, g := range groups {
|
||||
if gm, ok := g.(map[string]any); ok {
|
||||
group := pages_module.ComparisonGroupConfig{Name: strVal(gm, "name")}
|
||||
if features, ok := gm["features"].([]any); ok {
|
||||
for _, f := range features {
|
||||
if fm, ok := f.(map[string]any); ok {
|
||||
feature := pages_module.ComparisonFeatureConfig{Name: strVal(fm, "name")}
|
||||
if vals, ok := fm["values"].([]any); ok {
|
||||
for _, v := range vals {
|
||||
if s, ok := v.(string); ok {
|
||||
feature.Values = append(feature.Values, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
group.Features = append(group.Features, feature)
|
||||
}
|
||||
}
|
||||
}
|
||||
config.Comparison.Groups = append(config.Comparison.Groups, group)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return saveAndReturn(ctx, repoObj, config, "comparison")
|
||||
}
|
||||
|
||||
func toolUpdateLandingFeatures(ctx *context.APIContext, args map[string]any) (any, error) {
|
||||
config, repoObj, err := getConfigForUpdate(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if features, ok := args["features"].([]any); ok {
|
||||
config.Features = nil
|
||||
for _, f := range features {
|
||||
if fm, ok := f.(map[string]any); ok {
|
||||
config.Features = append(config.Features, pages_module.FeatureConfig{
|
||||
Title: strVal(fm, "title"),
|
||||
Description: strVal(fm, "description"),
|
||||
Icon: strVal(fm, "icon"),
|
||||
ImageURL: strVal(fm, "image_url"),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return saveAndReturn(ctx, repoObj, config, "features")
|
||||
}
|
||||
|
||||
func toolUpdateLandingSocialProof(ctx *context.APIContext, args map[string]any) (any, error) {
|
||||
config, repoObj, err := getConfigForUpdate(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if logos, ok := args["logos"].([]any); ok {
|
||||
config.SocialProof.Logos = nil
|
||||
for _, l := range logos {
|
||||
if s, ok := l.(string); ok {
|
||||
config.SocialProof.Logos = append(config.SocialProof.Logos, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
if testimonials, ok := args["testimonials"].([]any); ok {
|
||||
config.SocialProof.Testimonials = nil
|
||||
for _, t := range testimonials {
|
||||
if tm, ok := t.(map[string]any); ok {
|
||||
config.SocialProof.Testimonials = append(config.SocialProof.Testimonials, pages_module.TestimonialConfig{
|
||||
Quote: strVal(tm, "quote"),
|
||||
Author: strVal(tm, "author"),
|
||||
Role: strVal(tm, "role"),
|
||||
Avatar: strVal(tm, "avatar"),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return saveAndReturn(ctx, repoObj, config, "social_proof")
|
||||
}
|
||||
|
||||
func toolUpdateLandingSEO(ctx *context.APIContext, args map[string]any) (any, error) {
|
||||
config, repoObj, err := getConfigForUpdate(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if v, ok := args["title"].(string); ok {
|
||||
config.SEO.Title = v
|
||||
}
|
||||
if v, ok := args["description"].(string); ok {
|
||||
config.SEO.Description = v
|
||||
}
|
||||
if v, ok := args["og_image"].(string); ok {
|
||||
config.SEO.OGImage = v
|
||||
}
|
||||
if v, ok := args["twitter_card"].(string); ok {
|
||||
config.SEO.TwitterCard = v
|
||||
}
|
||||
if v, ok := args["twitter_site"].(string); ok {
|
||||
config.SEO.TwitterSite = v
|
||||
}
|
||||
if keywords, ok := args["keywords"].([]any); ok {
|
||||
config.SEO.Keywords = nil
|
||||
for _, k := range keywords {
|
||||
if s, ok := k.(string); ok {
|
||||
config.SEO.Keywords = append(config.SEO.Keywords, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return saveAndReturn(ctx, repoObj, config, "seo")
|
||||
}
|
||||
|
||||
func toolUpdateLandingTheme(ctx *context.APIContext, args map[string]any) (any, error) {
|
||||
config, repoObj, err := getConfigForUpdate(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if v, ok := args["primary_color"].(string); ok {
|
||||
config.Theme.PrimaryColor = v
|
||||
}
|
||||
if v, ok := args["accent_color"].(string); ok {
|
||||
config.Theme.AccentColor = v
|
||||
}
|
||||
if v, ok := args["mode"].(string); ok {
|
||||
config.Theme.Mode = v
|
||||
}
|
||||
|
||||
return saveAndReturn(ctx, repoObj, config, "theme")
|
||||
}
|
||||
|
||||
func toolUpdateLandingStats(ctx *context.APIContext, args map[string]any) (any, error) {
|
||||
config, repoObj, err := getConfigForUpdate(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if stats, ok := args["stats"].([]any); ok {
|
||||
config.Stats = nil
|
||||
for _, s := range stats {
|
||||
if sm, ok := s.(map[string]any); ok {
|
||||
config.Stats = append(config.Stats, pages_module.StatConfig{
|
||||
Value: strVal(sm, "value"),
|
||||
Label: strVal(sm, "label"),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return saveAndReturn(ctx, repoObj, config, "stats")
|
||||
}
|
||||
|
||||
func toolUpdateLandingValueProps(ctx *context.APIContext, args map[string]any) (any, error) {
|
||||
config, repoObj, err := getConfigForUpdate(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if vps, ok := args["value_props"].([]any); ok {
|
||||
config.ValueProps = nil
|
||||
for _, v := range vps {
|
||||
if vm, ok := v.(map[string]any); ok {
|
||||
config.ValueProps = append(config.ValueProps, pages_module.ValuePropConfig{
|
||||
Title: strVal(vm, "title"),
|
||||
Description: strVal(vm, "description"),
|
||||
Icon: strVal(vm, "icon"),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return saveAndReturn(ctx, repoObj, config, "value_props")
|
||||
}
|
||||
|
||||
func toolUpdateLandingCTA(ctx *context.APIContext, args map[string]any) (any, error) {
|
||||
config, repoObj, err := getConfigForUpdate(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if v, ok := args["headline"].(string); ok {
|
||||
config.CTASection.Headline = v
|
||||
}
|
||||
if v, ok := args["subheadline"].(string); ok {
|
||||
config.CTASection.Subheadline = v
|
||||
}
|
||||
if v, ok := args["button_label"].(string); ok {
|
||||
config.CTASection.Button.Label = v
|
||||
}
|
||||
if v, ok := args["button_url"].(string); ok {
|
||||
config.CTASection.Button.URL = v
|
||||
}
|
||||
|
||||
return saveAndReturn(ctx, repoObj, config, "cta_section")
|
||||
}
|
||||
|
||||
// ── Helpers ──────────────────────────────────
|
||||
|
||||
func getConfigForUpdate(ctx *context.APIContext, args map[string]any) (*pages_module.LandingConfig, *repo_model.Repository, error) {
|
||||
owner, repo, err := resolveOwnerRepo(args)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
repoObj, err := getRepoByOwnerAndName(ctx, owner, repo)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
config, err := pages_service.GetPagesConfig(ctx, repoObj)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("get config: %w", err)
|
||||
}
|
||||
if config == nil {
|
||||
config = pages_module.DefaultConfig()
|
||||
}
|
||||
|
||||
return config, repoObj, nil
|
||||
}
|
||||
|
||||
func saveAndReturn(ctx *context.APIContext, repo *repo_model.Repository, config *pages_module.LandingConfig, section string) (any, error) {
|
||||
configJSON, _ := json.Marshal(config)
|
||||
hash := pages_module.HashConfig(configJSON)
|
||||
|
||||
existing, _ := repo_model.GetPagesConfigByRepoID(ctx, repo.ID)
|
||||
|
||||
if existing != nil {
|
||||
existing.ConfigJSON = string(configJSON)
|
||||
existing.ConfigHash = hash
|
||||
existing.Template = repo_model.PagesTemplate(config.Template)
|
||||
existing.Enabled = config.Enabled
|
||||
if err := repo_model.UpdatePagesConfig(ctx, existing); err != nil {
|
||||
return nil, fmt.Errorf("save config: %w", err)
|
||||
}
|
||||
} else {
|
||||
if err := repo_model.CreatePagesConfig(ctx, &repo_model.PagesConfig{
|
||||
RepoID: repo.ID,
|
||||
Enabled: config.Enabled,
|
||||
Template: repo_model.PagesTemplate(config.Template),
|
||||
ConfigJSON: string(configJSON),
|
||||
ConfigHash: hash,
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("create config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"success": true,
|
||||
"section": section,
|
||||
"message": fmt.Sprintf("Updated %s section", section),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func strVal(m map[string]any, key string) string {
|
||||
if v, ok := m[key].(string); ok {
|
||||
return v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func boolVal(m map[string]any, key string) bool {
|
||||
if v, ok := m[key].(bool); ok {
|
||||
return v
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func resolveOwnerRepo(args map[string]any) (string, string, error) {
|
||||
owner, _ := args["owner"].(string)
|
||||
repo, _ := args["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return "", "", errors.New("owner and repo are required")
|
||||
}
|
||||
return owner, repo, nil
|
||||
}
|
||||
|
||||
func getRepoByOwnerAndName(ctx *context.APIContext, owner, repo string) (*repo_model.Repository, error) {
|
||||
ownerObj, err := user_model.GetUserByName(ctx, owner)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("owner not found: %s", owner)
|
||||
}
|
||||
repoObj, err := repo_model.GetRepositoryByName(ctx, ownerObj.ID, repo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("repo not found: %s/%s", owner, repo)
|
||||
}
|
||||
return repoObj, nil
|
||||
}
|
||||
@@ -7,22 +7,39 @@ import (
|
||||
"net/http"
|
||||
|
||||
repo_model "code.gitcaddy.com/server/v3/models/repo"
|
||||
apierrors "code.gitcaddy.com/server/v3/modules/errors"
|
||||
"code.gitcaddy.com/server/v3/modules/git"
|
||||
"code.gitcaddy.com/server/v3/modules/json"
|
||||
pages_module "code.gitcaddy.com/server/v3/modules/pages"
|
||||
api "code.gitcaddy.com/server/v3/modules/structs"
|
||||
"code.gitcaddy.com/server/v3/modules/web"
|
||||
"code.gitcaddy.com/server/v3/services/context"
|
||||
pages_service "code.gitcaddy.com/server/v3/services/pages"
|
||||
)
|
||||
|
||||
// PagesConfigResponse represents the pages configuration for a repository
|
||||
type PagesConfigResponse struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
PublicLanding bool `json:"public_landing"`
|
||||
Template string `json:"template"`
|
||||
Domain string `json:"domain,omitempty"`
|
||||
Brand pages_module.BrandConfig `json:"brand"`
|
||||
Hero pages_module.HeroConfig `json:"hero"`
|
||||
SEO pages_module.SEOConfig `json:"seo"`
|
||||
Footer pages_module.FooterConfig `json:"footer"`
|
||||
// PagesFullConfigResponse represents the complete landing page configuration
|
||||
type PagesFullConfigResponse struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
PublicLanding bool `json:"public_landing"`
|
||||
Template string `json:"template"`
|
||||
Domain string `json:"domain,omitempty"`
|
||||
Brand pages_module.BrandConfig `json:"brand"`
|
||||
Hero pages_module.HeroConfig `json:"hero"`
|
||||
Stats []pages_module.StatConfig `json:"stats"`
|
||||
ValueProps []pages_module.ValuePropConfig `json:"value_props"`
|
||||
Features []pages_module.FeatureConfig `json:"features"`
|
||||
SocialProof pages_module.SocialProofConfig `json:"social_proof"`
|
||||
Pricing pages_module.PricingConfig `json:"pricing"`
|
||||
CTASection pages_module.CTASectionConfig `json:"cta_section"`
|
||||
Blog pages_module.BlogSectionConfig `json:"blog"`
|
||||
Gallery pages_module.GallerySectionConfig `json:"gallery"`
|
||||
Comparison pages_module.ComparisonSectionConfig `json:"comparison"`
|
||||
Navigation pages_module.NavigationConfig `json:"navigation"`
|
||||
Footer pages_module.FooterConfig `json:"footer"`
|
||||
Theme pages_module.ThemeConfig `json:"theme"`
|
||||
SEO pages_module.SEOConfig `json:"seo"`
|
||||
Analytics pages_module.AnalyticsConfig `json:"analytics"`
|
||||
Advanced pages_module.AdvancedConfig `json:"advanced"`
|
||||
}
|
||||
|
||||
// PagesContentResponse represents the rendered content for a landing page
|
||||
@@ -32,7 +49,433 @@ type PagesContentResponse struct {
|
||||
Readme string `json:"readme,omitempty"`
|
||||
}
|
||||
|
||||
// GetPagesConfig returns the pages configuration for a repository
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func buildFullResponse(config *pages_module.LandingConfig) *PagesFullConfigResponse {
|
||||
return &PagesFullConfigResponse{
|
||||
Enabled: config.Enabled,
|
||||
PublicLanding: config.PublicLanding,
|
||||
Template: config.Template,
|
||||
Domain: config.Domain,
|
||||
Brand: config.Brand,
|
||||
Hero: config.Hero,
|
||||
Stats: config.Stats,
|
||||
ValueProps: config.ValueProps,
|
||||
Features: config.Features,
|
||||
SocialProof: config.SocialProof,
|
||||
Pricing: config.Pricing,
|
||||
CTASection: config.CTASection,
|
||||
Blog: config.Blog,
|
||||
Gallery: config.Gallery,
|
||||
Comparison: config.Comparison,
|
||||
Navigation: config.Navigation,
|
||||
Footer: config.Footer,
|
||||
Theme: config.Theme,
|
||||
SEO: config.SEO,
|
||||
Analytics: config.Analytics,
|
||||
Advanced: config.Advanced,
|
||||
}
|
||||
}
|
||||
|
||||
func getPagesConfigAPI(ctx *context.APIContext) (*pages_module.LandingConfig, bool) {
|
||||
config, err := pages_service.GetPagesConfig(ctx, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
ctx.APIErrorWithCode(apierrors.PagesNotConfigured)
|
||||
return nil, false
|
||||
}
|
||||
return config, true
|
||||
}
|
||||
|
||||
func savePagesConfigAPI(ctx *context.APIContext, config *pages_module.LandingConfig) bool {
|
||||
configJSON, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return false
|
||||
}
|
||||
|
||||
dbConfig, err := repo_model.GetPagesConfigByRepoID(ctx, ctx.Repo.Repository.ID)
|
||||
if err != nil {
|
||||
if repo_model.IsErrPagesConfigNotExist(err) {
|
||||
dbConfig = &repo_model.PagesConfig{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
Enabled: config.Enabled,
|
||||
Template: repo_model.PagesTemplate(config.Template),
|
||||
ConfigJSON: string(configJSON),
|
||||
}
|
||||
if err := repo_model.CreatePagesConfig(ctx, dbConfig); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
ctx.APIErrorInternal(err)
|
||||
return false
|
||||
}
|
||||
|
||||
dbConfig.Enabled = config.Enabled
|
||||
dbConfig.Template = repo_model.PagesTemplate(config.Template)
|
||||
dbConfig.ConfigJSON = string(configJSON)
|
||||
if err := repo_model.UpdatePagesConfig(ctx, dbConfig); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func requirePagesAdmin(ctx *context.APIContext) bool {
|
||||
if !ctx.Repo.Permission.IsAdmin() && !ctx.IsUserSiteAdmin() {
|
||||
ctx.APIErrorWithCode(apierrors.PermRepoAdminRequired)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Apply helpers — map API option structs to config structs
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func applyCTAButton(dst *pages_module.CTAButton, src *api.PagesCTAButtonOption) {
|
||||
if src == nil {
|
||||
return
|
||||
}
|
||||
if src.Label != nil {
|
||||
dst.Label = *src.Label
|
||||
}
|
||||
if src.URL != nil {
|
||||
dst.URL = *src.URL
|
||||
}
|
||||
if src.Variant != nil {
|
||||
dst.Variant = *src.Variant
|
||||
}
|
||||
}
|
||||
|
||||
func applyBrand(dst *pages_module.BrandConfig, src *api.UpdatePagesBrandOption) {
|
||||
if src.Name != nil {
|
||||
dst.Name = *src.Name
|
||||
}
|
||||
if src.LogoURL != nil {
|
||||
dst.LogoURL = *src.LogoURL
|
||||
}
|
||||
if src.Tagline != nil {
|
||||
dst.Tagline = *src.Tagline
|
||||
}
|
||||
if src.FaviconURL != nil {
|
||||
dst.FaviconURL = *src.FaviconURL
|
||||
}
|
||||
}
|
||||
|
||||
func applyHero(dst *pages_module.HeroConfig, src *api.UpdatePagesHeroOption) {
|
||||
if src.Headline != nil {
|
||||
dst.Headline = *src.Headline
|
||||
}
|
||||
if src.Subheadline != nil {
|
||||
dst.Subheadline = *src.Subheadline
|
||||
}
|
||||
if src.ImageURL != nil {
|
||||
dst.ImageURL = *src.ImageURL
|
||||
}
|
||||
if src.VideoURL != nil {
|
||||
dst.VideoURL = *src.VideoURL
|
||||
}
|
||||
if src.CodeExample != nil {
|
||||
dst.CodeExample = *src.CodeExample
|
||||
}
|
||||
applyCTAButton(&dst.PrimaryCTA, src.PrimaryCTA)
|
||||
applyCTAButton(&dst.SecondaryCTA, src.SecondaryCTA)
|
||||
}
|
||||
|
||||
func applyStats(config *pages_module.LandingConfig, src []api.PagesStatOption) {
|
||||
config.Stats = make([]pages_module.StatConfig, len(src))
|
||||
for i, s := range src {
|
||||
config.Stats[i] = pages_module.StatConfig{Value: s.Value, Label: s.Label}
|
||||
}
|
||||
}
|
||||
|
||||
func applyValueProps(config *pages_module.LandingConfig, src []api.PagesValuePropOption) {
|
||||
config.ValueProps = make([]pages_module.ValuePropConfig, len(src))
|
||||
for i, v := range src {
|
||||
config.ValueProps[i] = pages_module.ValuePropConfig{Title: v.Title, Description: v.Description, Icon: v.Icon}
|
||||
}
|
||||
}
|
||||
|
||||
func applyFeatures(config *pages_module.LandingConfig, src []api.PagesFeatureOption) {
|
||||
config.Features = make([]pages_module.FeatureConfig, len(src))
|
||||
for i, f := range src {
|
||||
config.Features[i] = pages_module.FeatureConfig{Title: f.Title, Description: f.Description, Icon: f.Icon, ImageURL: f.ImageURL}
|
||||
}
|
||||
}
|
||||
|
||||
func applySocial(dst *pages_module.SocialProofConfig, src *api.UpdatePagesSocialOption) {
|
||||
if src.Logos != nil {
|
||||
dst.Logos = *src.Logos
|
||||
}
|
||||
if src.Testimonials != nil {
|
||||
dst.Testimonials = make([]pages_module.TestimonialConfig, len(*src.Testimonials))
|
||||
for i, t := range *src.Testimonials {
|
||||
dst.Testimonials[i] = pages_module.TestimonialConfig{Quote: t.Quote, Author: t.Author, Role: t.Role, Avatar: t.Avatar}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func applyPricing(dst *pages_module.PricingConfig, src *api.UpdatePagesPricingOption) {
|
||||
if src.Headline != nil {
|
||||
dst.Headline = *src.Headline
|
||||
}
|
||||
if src.Subheadline != nil {
|
||||
dst.Subheadline = *src.Subheadline
|
||||
}
|
||||
if src.Plans != nil {
|
||||
dst.Plans = make([]pages_module.PricingPlanConfig, len(*src.Plans))
|
||||
for i, p := range *src.Plans {
|
||||
dst.Plans[i] = pages_module.PricingPlanConfig{
|
||||
Name: p.Name, Price: p.Price, Period: p.Period,
|
||||
Features: p.Features, CTA: p.CTA, Featured: p.Featured,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func applyCTASection(dst *pages_module.CTASectionConfig, src *api.UpdatePagesCTAOption) {
|
||||
if src.Headline != nil {
|
||||
dst.Headline = *src.Headline
|
||||
}
|
||||
if src.Subheadline != nil {
|
||||
dst.Subheadline = *src.Subheadline
|
||||
}
|
||||
applyCTAButton(&dst.Button, src.Button)
|
||||
}
|
||||
|
||||
func applyBlog(dst *pages_module.BlogSectionConfig, src *api.UpdatePagesBlogOption) {
|
||||
if src.Enabled != nil {
|
||||
dst.Enabled = *src.Enabled
|
||||
}
|
||||
if src.Headline != nil {
|
||||
dst.Headline = *src.Headline
|
||||
}
|
||||
if src.Subheadline != nil {
|
||||
dst.Subheadline = *src.Subheadline
|
||||
}
|
||||
if src.MaxPosts != nil {
|
||||
dst.MaxPosts = *src.MaxPosts
|
||||
}
|
||||
}
|
||||
|
||||
func applyGallery(dst *pages_module.GallerySectionConfig, src *api.UpdatePagesGalleryOption) {
|
||||
if src.Enabled != nil {
|
||||
dst.Enabled = *src.Enabled
|
||||
}
|
||||
if src.Headline != nil {
|
||||
dst.Headline = *src.Headline
|
||||
}
|
||||
if src.Subheadline != nil {
|
||||
dst.Subheadline = *src.Subheadline
|
||||
}
|
||||
if src.MaxImages != nil {
|
||||
dst.MaxImages = *src.MaxImages
|
||||
}
|
||||
if src.Columns != nil {
|
||||
dst.Columns = *src.Columns
|
||||
}
|
||||
}
|
||||
|
||||
func applyComparison(dst *pages_module.ComparisonSectionConfig, src *api.UpdatePagesComparisonOption) {
|
||||
if src.Enabled != nil {
|
||||
dst.Enabled = *src.Enabled
|
||||
}
|
||||
if src.Headline != nil {
|
||||
dst.Headline = *src.Headline
|
||||
}
|
||||
if src.Subheadline != nil {
|
||||
dst.Subheadline = *src.Subheadline
|
||||
}
|
||||
if src.Columns != nil {
|
||||
dst.Columns = make([]pages_module.ComparisonColumnConfig, len(*src.Columns))
|
||||
for i, c := range *src.Columns {
|
||||
dst.Columns[i] = pages_module.ComparisonColumnConfig{Name: c.Name, Highlight: c.Highlight}
|
||||
}
|
||||
}
|
||||
if src.Groups != nil {
|
||||
dst.Groups = make([]pages_module.ComparisonGroupConfig, len(*src.Groups))
|
||||
for i, g := range *src.Groups {
|
||||
features := make([]pages_module.ComparisonFeatureConfig, len(g.Features))
|
||||
for j, f := range g.Features {
|
||||
features[j] = pages_module.ComparisonFeatureConfig{Name: f.Name, Values: f.Values}
|
||||
}
|
||||
dst.Groups[i] = pages_module.ComparisonGroupConfig{Name: g.Name, Features: features}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func applyNavigation(dst *pages_module.NavigationConfig, src *api.UpdatePagesNavOption) {
|
||||
if src.ShowDocs != nil {
|
||||
dst.ShowDocs = *src.ShowDocs
|
||||
}
|
||||
if src.ShowAPI != nil {
|
||||
dst.ShowAPI = *src.ShowAPI
|
||||
}
|
||||
if src.ShowRepository != nil {
|
||||
dst.ShowRepository = *src.ShowRepository
|
||||
}
|
||||
if src.ShowReleases != nil {
|
||||
dst.ShowReleases = *src.ShowReleases
|
||||
}
|
||||
if src.ShowIssues != nil {
|
||||
dst.ShowIssues = *src.ShowIssues
|
||||
}
|
||||
}
|
||||
|
||||
func applyFooter(dst *pages_module.FooterConfig, ctaDst *pages_module.CTASectionConfig, src *api.UpdatePagesFooterOption) {
|
||||
if src.Copyright != nil {
|
||||
dst.Copyright = *src.Copyright
|
||||
}
|
||||
if src.ShowPoweredBy != nil {
|
||||
dst.ShowPoweredBy = *src.ShowPoweredBy
|
||||
}
|
||||
if src.Links != nil {
|
||||
dst.Links = make([]pages_module.FooterLink, len(*src.Links))
|
||||
for i, l := range *src.Links {
|
||||
dst.Links[i] = pages_module.FooterLink{Label: l.Label, URL: l.URL}
|
||||
}
|
||||
}
|
||||
if src.Social != nil {
|
||||
dst.Social = make([]pages_module.SocialLink, len(*src.Social))
|
||||
for i, s := range *src.Social {
|
||||
dst.Social[i] = pages_module.SocialLink{Platform: s.Platform, URL: s.URL}
|
||||
}
|
||||
}
|
||||
if src.CTASection != nil {
|
||||
applyCTASection(ctaDst, src.CTASection)
|
||||
}
|
||||
}
|
||||
|
||||
func applyTheme(dst *pages_module.ThemeConfig, src *api.UpdatePagesThemeOption) {
|
||||
if src.PrimaryColor != nil {
|
||||
dst.PrimaryColor = *src.PrimaryColor
|
||||
}
|
||||
if src.AccentColor != nil {
|
||||
dst.AccentColor = *src.AccentColor
|
||||
}
|
||||
if src.Mode != nil {
|
||||
dst.Mode = *src.Mode
|
||||
}
|
||||
}
|
||||
|
||||
func applySEO(dst *pages_module.SEOConfig, src *api.UpdatePagesSEOOption) {
|
||||
if src.Title != nil {
|
||||
dst.Title = *src.Title
|
||||
}
|
||||
if src.Description != nil {
|
||||
dst.Description = *src.Description
|
||||
}
|
||||
if src.Keywords != nil {
|
||||
dst.Keywords = *src.Keywords
|
||||
}
|
||||
if src.OGImage != nil {
|
||||
dst.OGImage = *src.OGImage
|
||||
}
|
||||
if src.UseMediaKitOG != nil {
|
||||
dst.UseMediaKitOG = *src.UseMediaKitOG
|
||||
}
|
||||
if src.TwitterCard != nil {
|
||||
dst.TwitterCard = *src.TwitterCard
|
||||
}
|
||||
if src.TwitterSite != nil {
|
||||
dst.TwitterSite = *src.TwitterSite
|
||||
}
|
||||
}
|
||||
|
||||
func applyAdvanced(dst *pages_module.AdvancedConfig, src *api.UpdatePagesAdvancedOption) {
|
||||
if src.CustomCSS != nil {
|
||||
dst.CustomCSS = *src.CustomCSS
|
||||
}
|
||||
if src.CustomHead != nil {
|
||||
dst.CustomHead = *src.CustomHead
|
||||
}
|
||||
if src.StaticRoutes != nil {
|
||||
dst.StaticRoutes = *src.StaticRoutes
|
||||
}
|
||||
if src.PublicReleases != nil {
|
||||
dst.PublicReleases = *src.PublicReleases
|
||||
}
|
||||
if src.HideMobileReleases != nil {
|
||||
dst.HideMobileReleases = *src.HideMobileReleases
|
||||
}
|
||||
if src.GooglePlayID != nil {
|
||||
dst.GooglePlayID = *src.GooglePlayID
|
||||
}
|
||||
if src.AppStoreID != nil {
|
||||
dst.AppStoreID = *src.AppStoreID
|
||||
}
|
||||
}
|
||||
|
||||
// applyFullConfig applies all non-nil sections from the update option to the config
|
||||
func applyFullConfig(config *pages_module.LandingConfig, form *api.UpdatePagesConfigOption) {
|
||||
if form.Enabled != nil {
|
||||
config.Enabled = *form.Enabled
|
||||
}
|
||||
if form.PublicLanding != nil {
|
||||
config.PublicLanding = *form.PublicLanding
|
||||
}
|
||||
if form.Template != nil {
|
||||
config.Template = *form.Template
|
||||
}
|
||||
if form.Brand != nil {
|
||||
applyBrand(&config.Brand, form.Brand)
|
||||
}
|
||||
if form.Hero != nil {
|
||||
applyHero(&config.Hero, form.Hero)
|
||||
}
|
||||
if form.Stats != nil {
|
||||
applyStats(config, *form.Stats)
|
||||
}
|
||||
if form.ValueProps != nil {
|
||||
applyValueProps(config, *form.ValueProps)
|
||||
}
|
||||
if form.Features != nil {
|
||||
applyFeatures(config, *form.Features)
|
||||
}
|
||||
if form.SocialProof != nil {
|
||||
applySocial(&config.SocialProof, form.SocialProof)
|
||||
}
|
||||
if form.Pricing != nil {
|
||||
applyPricing(&config.Pricing, form.Pricing)
|
||||
}
|
||||
if form.CTASection != nil {
|
||||
applyCTASection(&config.CTASection, form.CTASection)
|
||||
}
|
||||
if form.Blog != nil {
|
||||
applyBlog(&config.Blog, form.Blog)
|
||||
}
|
||||
if form.Gallery != nil {
|
||||
applyGallery(&config.Gallery, form.Gallery)
|
||||
}
|
||||
if form.Comparison != nil {
|
||||
applyComparison(&config.Comparison, form.Comparison)
|
||||
}
|
||||
if form.Navigation != nil {
|
||||
applyNavigation(&config.Navigation, form.Navigation)
|
||||
}
|
||||
if form.Footer != nil {
|
||||
applyFooter(&config.Footer, &config.CTASection, form.Footer)
|
||||
}
|
||||
if form.Theme != nil {
|
||||
applyTheme(&config.Theme, form.Theme)
|
||||
}
|
||||
if form.SEO != nil {
|
||||
applySEO(&config.SEO, form.SEO)
|
||||
}
|
||||
if form.Advanced != nil {
|
||||
applyAdvanced(&config.Advanced, form.Advanced)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GET endpoints
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// GetPagesConfig returns the full pages configuration for a repository
|
||||
// GET /api/v2/repos/{owner}/{repo}/pages/config
|
||||
func GetPagesConfig(ctx *context.APIContext) {
|
||||
repo := ctx.Repo.Repository
|
||||
@@ -43,22 +486,11 @@ func GetPagesConfig(ctx *context.APIContext) {
|
||||
|
||||
config, err := pages_service.GetPagesConfig(ctx, repo)
|
||||
if err != nil {
|
||||
ctx.APIErrorNotFound("Pages not configured")
|
||||
ctx.APIErrorWithCode(apierrors.PagesNotConfigured)
|
||||
return
|
||||
}
|
||||
|
||||
response := &PagesConfigResponse{
|
||||
Enabled: config.Enabled,
|
||||
PublicLanding: config.PublicLanding,
|
||||
Template: config.Template,
|
||||
Domain: config.Domain,
|
||||
Brand: config.Brand,
|
||||
Hero: config.Hero,
|
||||
SEO: config.SEO,
|
||||
Footer: config.Footer,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, response)
|
||||
ctx.JSON(http.StatusOK, buildFullResponse(config))
|
||||
}
|
||||
|
||||
// GetPagesContent returns the rendered content for a repository's landing page
|
||||
@@ -72,14 +504,12 @@ func GetPagesContent(ctx *context.APIContext) {
|
||||
|
||||
config, err := pages_service.GetPagesConfig(ctx, repo)
|
||||
if err != nil || !config.Enabled {
|
||||
ctx.APIErrorNotFound("Pages not enabled")
|
||||
ctx.APIErrorWithCode(apierrors.PagesNotEnabled)
|
||||
return
|
||||
}
|
||||
|
||||
// Load README content
|
||||
readme := loadReadmeContent(ctx, repo)
|
||||
|
||||
// Build title
|
||||
title := config.SEO.Title
|
||||
if title == "" {
|
||||
title = config.Hero.Headline
|
||||
@@ -91,7 +521,6 @@ func GetPagesContent(ctx *context.APIContext) {
|
||||
title = repo.Name
|
||||
}
|
||||
|
||||
// Build description
|
||||
description := config.SEO.Description
|
||||
if description == "" {
|
||||
description = config.Hero.Subheadline
|
||||
@@ -100,15 +529,265 @@ func GetPagesContent(ctx *context.APIContext) {
|
||||
description = repo.Description
|
||||
}
|
||||
|
||||
response := &PagesContentResponse{
|
||||
ctx.JSON(http.StatusOK, &PagesContentResponse{
|
||||
Title: title,
|
||||
Description: description,
|
||||
Readme: readme,
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// PUT /config — replace full config
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// UpdatePagesConfig replaces the entire landing page configuration
|
||||
// PUT /api/v2/repos/{owner}/{repo}/pages/config
|
||||
func UpdatePagesConfig(ctx *context.APIContext) {
|
||||
if !requirePagesAdmin(ctx) {
|
||||
return
|
||||
}
|
||||
config, ok := getPagesConfigAPI(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, response)
|
||||
form := web.GetForm(ctx).(*api.UpdatePagesConfigOption)
|
||||
|
||||
if form.Template != nil && !pages_module.IsValidTemplate(*form.Template) {
|
||||
ctx.APIErrorWithCode(apierrors.PagesInvalidTemplate)
|
||||
return
|
||||
}
|
||||
|
||||
applyFullConfig(config, form)
|
||||
|
||||
if !savePagesConfigAPI(ctx, config) {
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, buildFullResponse(config))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// PATCH /config — partial merge
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// PatchPagesConfig partially updates the landing page configuration
|
||||
// PATCH /api/v2/repos/{owner}/{repo}/pages/config
|
||||
func PatchPagesConfig(ctx *context.APIContext) {
|
||||
if !requirePagesAdmin(ctx) {
|
||||
return
|
||||
}
|
||||
config, ok := getPagesConfigAPI(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
form := web.GetForm(ctx).(*api.UpdatePagesConfigOption)
|
||||
|
||||
if form.Template != nil && !pages_module.IsValidTemplate(*form.Template) {
|
||||
ctx.APIErrorWithCode(apierrors.PagesInvalidTemplate)
|
||||
return
|
||||
}
|
||||
|
||||
applyFullConfig(config, form)
|
||||
|
||||
if !savePagesConfigAPI(ctx, config) {
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, buildFullResponse(config))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Section PUT endpoints
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// UpdatePagesBrand updates the brand section
|
||||
// PUT /api/v2/repos/{owner}/{repo}/pages/config/brand
|
||||
func UpdatePagesBrand(ctx *context.APIContext) {
|
||||
if !requirePagesAdmin(ctx) {
|
||||
return
|
||||
}
|
||||
config, ok := getPagesConfigAPI(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
form := web.GetForm(ctx).(*api.UpdatePagesBrandOption)
|
||||
applyBrand(&config.Brand, form)
|
||||
|
||||
if !savePagesConfigAPI(ctx, config) {
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, buildFullResponse(config))
|
||||
}
|
||||
|
||||
// UpdatePagesHero updates the hero section
|
||||
// PUT /api/v2/repos/{owner}/{repo}/pages/config/hero
|
||||
func UpdatePagesHero(ctx *context.APIContext) {
|
||||
if !requirePagesAdmin(ctx) {
|
||||
return
|
||||
}
|
||||
config, ok := getPagesConfigAPI(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
form := web.GetForm(ctx).(*api.UpdatePagesHeroOption)
|
||||
applyHero(&config.Hero, form)
|
||||
|
||||
if !savePagesConfigAPI(ctx, config) {
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, buildFullResponse(config))
|
||||
}
|
||||
|
||||
// UpdatePagesContentSection updates the content section (blog, gallery, stats, features, nav, etc.)
|
||||
// PUT /api/v2/repos/{owner}/{repo}/pages/config/content
|
||||
func UpdatePagesContentSection(ctx *context.APIContext) {
|
||||
if !requirePagesAdmin(ctx) {
|
||||
return
|
||||
}
|
||||
config, ok := getPagesConfigAPI(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
form := web.GetForm(ctx).(*api.UpdatePagesContentOption)
|
||||
|
||||
if form.Blog != nil {
|
||||
applyBlog(&config.Blog, form.Blog)
|
||||
}
|
||||
if form.Gallery != nil {
|
||||
applyGallery(&config.Gallery, form.Gallery)
|
||||
}
|
||||
if form.ComparisonEnabled != nil {
|
||||
config.Comparison.Enabled = *form.ComparisonEnabled
|
||||
}
|
||||
if form.Stats != nil {
|
||||
applyStats(config, *form.Stats)
|
||||
}
|
||||
if form.ValueProps != nil {
|
||||
applyValueProps(config, *form.ValueProps)
|
||||
}
|
||||
if form.Features != nil {
|
||||
applyFeatures(config, *form.Features)
|
||||
}
|
||||
if form.Navigation != nil {
|
||||
applyNavigation(&config.Navigation, form.Navigation)
|
||||
}
|
||||
if form.Advanced != nil {
|
||||
applyAdvanced(&config.Advanced, form.Advanced)
|
||||
}
|
||||
|
||||
if !savePagesConfigAPI(ctx, config) {
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, buildFullResponse(config))
|
||||
}
|
||||
|
||||
// UpdatePagesComparison updates the comparison section
|
||||
// PUT /api/v2/repos/{owner}/{repo}/pages/config/comparison
|
||||
func UpdatePagesComparison(ctx *context.APIContext) {
|
||||
if !requirePagesAdmin(ctx) {
|
||||
return
|
||||
}
|
||||
config, ok := getPagesConfigAPI(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
form := web.GetForm(ctx).(*api.UpdatePagesComparisonOption)
|
||||
applyComparison(&config.Comparison, form)
|
||||
|
||||
if !savePagesConfigAPI(ctx, config) {
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, buildFullResponse(config))
|
||||
}
|
||||
|
||||
// UpdatePagesSocial updates the social proof section
|
||||
// PUT /api/v2/repos/{owner}/{repo}/pages/config/social
|
||||
func UpdatePagesSocial(ctx *context.APIContext) {
|
||||
if !requirePagesAdmin(ctx) {
|
||||
return
|
||||
}
|
||||
config, ok := getPagesConfigAPI(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
form := web.GetForm(ctx).(*api.UpdatePagesSocialOption)
|
||||
applySocial(&config.SocialProof, form)
|
||||
|
||||
if !savePagesConfigAPI(ctx, config) {
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, buildFullResponse(config))
|
||||
}
|
||||
|
||||
// UpdatePagesPricing updates the pricing section
|
||||
// PUT /api/v2/repos/{owner}/{repo}/pages/config/pricing
|
||||
func UpdatePagesPricing(ctx *context.APIContext) {
|
||||
if !requirePagesAdmin(ctx) {
|
||||
return
|
||||
}
|
||||
config, ok := getPagesConfigAPI(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
form := web.GetForm(ctx).(*api.UpdatePagesPricingOption)
|
||||
applyPricing(&config.Pricing, form)
|
||||
|
||||
if !savePagesConfigAPI(ctx, config) {
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, buildFullResponse(config))
|
||||
}
|
||||
|
||||
// UpdatePagesFooter updates the footer and CTA section
|
||||
// PUT /api/v2/repos/{owner}/{repo}/pages/config/footer
|
||||
func UpdatePagesFooter(ctx *context.APIContext) {
|
||||
if !requirePagesAdmin(ctx) {
|
||||
return
|
||||
}
|
||||
config, ok := getPagesConfigAPI(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
form := web.GetForm(ctx).(*api.UpdatePagesFooterOption)
|
||||
applyFooter(&config.Footer, &config.CTASection, form)
|
||||
|
||||
if !savePagesConfigAPI(ctx, config) {
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, buildFullResponse(config))
|
||||
}
|
||||
|
||||
// UpdatePagesTheme updates the theme and SEO section
|
||||
// PUT /api/v2/repos/{owner}/{repo}/pages/config/theme
|
||||
func UpdatePagesTheme(ctx *context.APIContext) {
|
||||
if !requirePagesAdmin(ctx) {
|
||||
return
|
||||
}
|
||||
config, ok := getPagesConfigAPI(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
form := web.GetForm(ctx).(*api.UpdatePagesThemeOption)
|
||||
applyTheme(&config.Theme, form)
|
||||
|
||||
if !savePagesConfigAPI(ctx, config) {
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, buildFullResponse(config))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Utility
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// loadReadmeContent loads the README content from the repository
|
||||
func loadReadmeContent(ctx *context.APIContext, repo *repo_model.Repository) string {
|
||||
gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())
|
||||
@@ -127,7 +806,6 @@ func loadReadmeContent(ctx *context.APIContext, repo *repo_model.Repository) str
|
||||
return ""
|
||||
}
|
||||
|
||||
// Try common README paths
|
||||
readmePaths := []string{
|
||||
"README.md",
|
||||
"readme.md",
|
||||
|
||||
@@ -148,6 +148,7 @@ func CheckAppUpdate(ctx *context.APIContext) {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
latestRelease.Repo = repo
|
||||
|
||||
// Find the appropriate asset for this platform/arch
|
||||
downloadURL, platformInfo := findUpdateAsset(latestRelease, platform, arch)
|
||||
@@ -346,6 +347,7 @@ func ListReleasesV2(ctx *context.APIContext) {
|
||||
// Convert to API format
|
||||
apiReleases := make([]*api.Release, 0, len(releases))
|
||||
for _, release := range releases {
|
||||
release.Repo = repo
|
||||
apiReleases = append(apiReleases, convertToAPIRelease(repo, release))
|
||||
}
|
||||
|
||||
@@ -388,6 +390,7 @@ func GetReleaseV2(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
release.Repo = repo
|
||||
ctx.JSON(http.StatusOK, convertToAPIRelease(repo, release))
|
||||
}
|
||||
|
||||
@@ -436,6 +439,7 @@ func GetLatestReleaseV2(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
release.Repo = repo
|
||||
ctx.JSON(http.StatusOK, convertToAPIRelease(repo, release))
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
_ "image/jpeg" // register JPEG decoder for social card background images
|
||||
_ "image/png" // register PNG decoder for social card background images
|
||||
"io"
|
||||
"maps"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"path"
|
||||
@@ -75,6 +76,15 @@ func ServeLandingPage(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check for static route file serving
|
||||
if len(config.Advanced.StaticRoutes) > 0 {
|
||||
cleanPath := path.Clean(requestPath)
|
||||
if matchesStaticRoute(cleanPath, config.Advanced.StaticRoutes) {
|
||||
serveStaticRouteFile(ctx, repo, cleanPath)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Handle event tracking POST
|
||||
if ctx.Req.Method == http.MethodPost && (requestPath == "/pages/events" || requestPath == "/pages/events/") {
|
||||
servePageEvent(ctx, repo)
|
||||
@@ -592,6 +602,89 @@ func serveCustomDomainAsset(ctx *context.Context, repo *repo_model.Repository, a
|
||||
serveRepoFileAsset(ctx, commit, assetPath)
|
||||
}
|
||||
|
||||
// matchesStaticRoute checks if a request path matches any configured static route pattern.
|
||||
// Supports exact matches (/badge.svg) and glob patterns (/schema/*).
|
||||
func matchesStaticRoute(requestPath string, routes []string) bool {
|
||||
for _, route := range routes {
|
||||
if route == requestPath {
|
||||
return true
|
||||
}
|
||||
// Handle glob patterns like /schema/*
|
||||
if matched, _ := path.Match(route, requestPath); matched {
|
||||
return true
|
||||
}
|
||||
// Handle prefix patterns: /schema/* should match /schema/sub/deep.json
|
||||
if strings.HasSuffix(route, "/*") {
|
||||
prefix := strings.TrimSuffix(route, "*")
|
||||
if strings.HasPrefix(requestPath, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// serveStaticRouteFile serves a file directly from the repo tree for static route matches.
|
||||
// The request path maps directly to the repo root (e.g., /schema/v0.1.json → schema/v0.1.json).
|
||||
func serveStaticRouteFile(ctx *context.Context, repo *repo_model.Repository, requestPath string) {
|
||||
// Strip leading slash to get repo-relative path
|
||||
repoPath := strings.TrimPrefix(requestPath, "/")
|
||||
if repoPath == "" {
|
||||
ctx.NotFound(errors.New("empty static route path"))
|
||||
return
|
||||
}
|
||||
|
||||
// Security: never serve .gitea config files
|
||||
if strings.HasPrefix(repoPath, ".gitea/") || strings.HasPrefix(repoPath, ".gitea\\") {
|
||||
ctx.NotFound(errors.New("config files are not served"))
|
||||
return
|
||||
}
|
||||
|
||||
gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())
|
||||
if err != nil {
|
||||
ctx.NotFound(err)
|
||||
return
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
branch := repo.DefaultBranch
|
||||
if branch == "" {
|
||||
branch = "main"
|
||||
}
|
||||
|
||||
commit, err := gitRepo.GetBranchCommit(branch)
|
||||
if err != nil {
|
||||
ctx.NotFound(err)
|
||||
return
|
||||
}
|
||||
|
||||
entry, err := commit.GetTreeEntryByPath(repoPath)
|
||||
if err != nil {
|
||||
ctx.NotFound(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Only serve blobs, not trees
|
||||
if !entry.IsRegular() {
|
||||
ctx.NotFound(errors.New("path is not a file"))
|
||||
return
|
||||
}
|
||||
|
||||
reader, err := entry.Blob().DataAsync()
|
||||
if err != nil {
|
||||
ctx.ServerError("Failed to read static route file", err)
|
||||
return
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
ext := path.Ext(repoPath)
|
||||
contentType := getContentType(ext)
|
||||
ctx.Resp.Header().Set("Content-Type", contentType)
|
||||
ctx.Resp.Header().Set("Cache-Control", "public, max-age=3600")
|
||||
ctx.Resp.Header().Set("Content-Length", strconv.FormatInt(entry.Blob().Size(), 10))
|
||||
_, _ = io.Copy(ctx.Resp, reader)
|
||||
}
|
||||
|
||||
// serveSocialPreview generates and serves the social card image for a repo
|
||||
// on custom domain / subdomain requests.
|
||||
func serveSocialPreview(ctx *context.Context, repo *repo_model.Repository) {
|
||||
@@ -1070,6 +1163,8 @@ func deepMergeConfig(base *pages_module.LandingConfig, overrideJSON string) (*pa
|
||||
}
|
||||
|
||||
// deepMerge recursively merges src into dst.
|
||||
// Maps are merged recursively; arrays are merged element-wise (preserving
|
||||
// base fields like icons that the translation overlay may omit).
|
||||
func deepMerge(dst, src map[string]any) map[string]any {
|
||||
for key, srcVal := range src {
|
||||
if dstVal, ok := dst[key]; ok {
|
||||
@@ -1080,12 +1175,45 @@ func deepMerge(dst, src map[string]any) map[string]any {
|
||||
dst[key] = deepMerge(dstMap, srcMap)
|
||||
continue
|
||||
}
|
||||
// Both are arrays: merge element-wise
|
||||
srcArr, srcIsArr := srcVal.([]any)
|
||||
dstArr, dstIsArr := dstVal.([]any)
|
||||
if srcIsArr && dstIsArr {
|
||||
dst[key] = deepMergeArrays(dstArr, srcArr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
dst[key] = srcVal
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// deepMergeArrays merges two arrays element-wise. For each index, if both
|
||||
// elements are maps, they are deep-merged (so translation fields override
|
||||
// base fields while preserving untranslated fields like icons). Otherwise
|
||||
// the source element replaces the base.
|
||||
func deepMergeArrays(dst, src []any) []any {
|
||||
result := make([]any, max(len(dst), len(src)))
|
||||
for i := range result {
|
||||
if i < len(src) && i < len(dst) {
|
||||
srcMap, srcIsMap := src[i].(map[string]any)
|
||||
dstMap, dstIsMap := dst[i].(map[string]any)
|
||||
if srcIsMap && dstIsMap {
|
||||
merged := make(map[string]any, len(dstMap))
|
||||
maps.Copy(merged, dstMap)
|
||||
result[i] = deepMerge(merged, srcMap)
|
||||
} else {
|
||||
result[i] = src[i]
|
||||
}
|
||||
} else if i < len(src) {
|
||||
result[i] = src[i]
|
||||
} else {
|
||||
result[i] = dst[i]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// detectPageLanguage determines the active language for a landing page.
|
||||
// Priority: ?lang= query param > pages_lang cookie > Accept-Language header > default.
|
||||
func detectPageLanguage(ctx *context.Context, config *pages_module.LandingConfig) string {
|
||||
@@ -1138,6 +1266,11 @@ func detectPageLanguage(ctx *context.Context, config *pages_module.LandingConfig
|
||||
// applyLanguageOverlay loads the translation for the detected language and merges it onto config.
|
||||
// Sets template data for the language switcher and returns the (possibly merged) config.
|
||||
func applyLanguageOverlay(ctx *context.Context, repo *repo_model.Repository, config *pages_module.LandingConfig) *pages_module.LandingConfig {
|
||||
// Ensure navigation labels and section headlines are populated with
|
||||
// template-specific defaults so they are present in the base config
|
||||
// for deep-merge (and so templates never fall back to hardcoded English).
|
||||
ensureTemplateDefaults(config)
|
||||
|
||||
if len(config.I18n.Languages) == 0 {
|
||||
return config
|
||||
}
|
||||
@@ -1179,6 +1312,44 @@ func applyLanguageOverlay(ctx *context.Context, repo *repo_model.Repository, con
|
||||
return merged
|
||||
}
|
||||
|
||||
// ensureTemplateDefaults fills in empty navigation labels and section headlines
|
||||
// with template-specific defaults so they are always present for serialization
|
||||
// and deep-merge. This ensures the base config JSON contains these keys, making
|
||||
// them available for the translation overlay to override.
|
||||
func ensureTemplateDefaults(config *pages_module.LandingConfig) {
|
||||
defaults := pages_module.TemplateDefaultLabels(config.Template)
|
||||
nav := &config.Navigation
|
||||
if nav.LabelValueProps == "" {
|
||||
nav.LabelValueProps = defaults.LabelValueProps
|
||||
}
|
||||
if nav.LabelFeatures == "" {
|
||||
nav.LabelFeatures = defaults.LabelFeatures
|
||||
}
|
||||
if nav.LabelPricing == "" {
|
||||
nav.LabelPricing = defaults.LabelPricing
|
||||
}
|
||||
if nav.LabelBlog == "" {
|
||||
nav.LabelBlog = defaults.LabelBlog
|
||||
}
|
||||
if nav.LabelGallery == "" {
|
||||
nav.LabelGallery = defaults.LabelGallery
|
||||
}
|
||||
if nav.LabelCompare == "" {
|
||||
nav.LabelCompare = defaults.LabelCompare
|
||||
}
|
||||
// Section headlines — fill empty headlines with sensible defaults
|
||||
// so they appear in the base JSON and can be overridden by translations.
|
||||
if config.Blog.Enabled && config.Blog.Headline == "" {
|
||||
config.Blog.Headline = "Latest Posts"
|
||||
}
|
||||
if config.Gallery.Enabled && config.Gallery.Headline == "" {
|
||||
config.Gallery.Headline = "Gallery"
|
||||
}
|
||||
if config.Comparison.Enabled && config.Comparison.Headline == "" {
|
||||
config.Comparison.Headline = "How We Compare"
|
||||
}
|
||||
}
|
||||
|
||||
// ApproveExperiment handles the email approval link for an A/B test experiment
|
||||
func ApproveExperiment(ctx *context.Context) {
|
||||
handleExperimentAction(ctx, true)
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
pages_model "code.gitcaddy.com/server/v3/models/pages"
|
||||
repo_model "code.gitcaddy.com/server/v3/models/repo"
|
||||
@@ -36,6 +37,7 @@ const (
|
||||
tplRepoSettingsPagesFooter templates.TplName = "repo/settings/pages_footer"
|
||||
tplRepoSettingsPagesTheme templates.TplName = "repo/settings/pages_theme"
|
||||
tplRepoSettingsPagesLanguages templates.TplName = "repo/settings/pages_languages"
|
||||
tplRepoSettingsPagesAdvanced templates.TplName = "repo/settings/pages_advanced"
|
||||
)
|
||||
|
||||
// getPagesLandingConfig loads the landing page configuration
|
||||
@@ -86,6 +88,31 @@ func savePagesLandingConfig(ctx *context.Context, config *pages_module.LandingCo
|
||||
return repo_model.UpdatePagesConfig(ctx, dbConfig)
|
||||
}
|
||||
|
||||
// applyTemplateDefaultLabels populates Navigation label fields with
|
||||
// template-specific defaults. Existing non-empty labels are preserved.
|
||||
func applyTemplateDefaultLabels(config *pages_module.LandingConfig) {
|
||||
defaults := pages_module.TemplateDefaultLabels(config.Template)
|
||||
nav := &config.Navigation
|
||||
if nav.LabelValueProps == "" {
|
||||
nav.LabelValueProps = defaults.LabelValueProps
|
||||
}
|
||||
if nav.LabelFeatures == "" {
|
||||
nav.LabelFeatures = defaults.LabelFeatures
|
||||
}
|
||||
if nav.LabelPricing == "" {
|
||||
nav.LabelPricing = defaults.LabelPricing
|
||||
}
|
||||
if nav.LabelBlog == "" {
|
||||
nav.LabelBlog = defaults.LabelBlog
|
||||
}
|
||||
if nav.LabelGallery == "" {
|
||||
nav.LabelGallery = defaults.LabelGallery
|
||||
}
|
||||
if nav.LabelCompare == "" {
|
||||
nav.LabelCompare = defaults.LabelCompare
|
||||
}
|
||||
}
|
||||
|
||||
// setCommonPagesData sets common data for all pages settings pages
|
||||
func setCommonPagesData(ctx *context.Context) {
|
||||
config := getPagesLandingConfig(ctx)
|
||||
@@ -140,6 +167,7 @@ func PagesPost(ctx *context.Context) {
|
||||
config := getPagesLandingConfig(ctx)
|
||||
config.Enabled = true
|
||||
config.Template = template
|
||||
applyTemplateDefaultLabels(config)
|
||||
if err := savePagesLandingConfig(ctx, config); err != nil {
|
||||
ctx.ServerError("EnablePages", err)
|
||||
return
|
||||
@@ -160,6 +188,7 @@ func PagesPost(ctx *context.Context) {
|
||||
}
|
||||
config := getPagesLandingConfig(ctx)
|
||||
config.Template = template
|
||||
applyTemplateDefaultLabels(config)
|
||||
if err := savePagesLandingConfig(ctx, config); err != nil {
|
||||
ctx.ServerError("UpdateTemplate", err)
|
||||
return
|
||||
@@ -716,19 +745,100 @@ func PagesThemePost(ctx *context.Context) {
|
||||
ctx.Redirect(ctx.Repo.Repository.Link() + "/settings/pages/theme")
|
||||
}
|
||||
|
||||
// TranslationView is a flattened view of a translation for the settings UI
|
||||
// TranslationView is a flattened view of a translation for the settings UI.
|
||||
// Slice fields are padded to match the base config array lengths.
|
||||
type TranslationView struct {
|
||||
Headline string
|
||||
Subheadline string
|
||||
PrimaryCTA string
|
||||
SecondaryCTA string
|
||||
// Brand
|
||||
BrandName string
|
||||
BrandTagline string
|
||||
// Hero
|
||||
Headline string
|
||||
Subheadline string
|
||||
PrimaryCTA string
|
||||
SecondaryCTA string
|
||||
// Stats (parallel to Config.Stats)
|
||||
StatsValues []string
|
||||
StatsLabels []string
|
||||
// Value Props (parallel to Config.ValueProps)
|
||||
ValuePropTitles []string
|
||||
ValuePropDescs []string
|
||||
// Features (parallel to Config.Features)
|
||||
FeatureTitles []string
|
||||
FeatureDescs []string
|
||||
// Testimonials (parallel to Config.SocialProof.Testimonials)
|
||||
TestimonialQuotes []string
|
||||
TestimonialRoles []string
|
||||
// Pricing
|
||||
PricingHeadline string
|
||||
PricingSubheadline string
|
||||
PlanNames []string
|
||||
PlanPeriods []string
|
||||
PlanCTAs []string
|
||||
// CTA Section
|
||||
CTAHeadline string
|
||||
CTASubheadline string
|
||||
CTAButton string
|
||||
// Blog
|
||||
BlogHeadline string
|
||||
BlogSubheadline string
|
||||
BlogCTAButton string
|
||||
// Gallery
|
||||
GalleryHeadline string
|
||||
GallerySubheadline string
|
||||
// Comparison
|
||||
ComparisonHeadline string
|
||||
ComparisonSubheadline string
|
||||
// Footer
|
||||
FooterCopyright string
|
||||
FooterLinkLabels []string
|
||||
// SEO
|
||||
SEOTitle string
|
||||
SEODescription string
|
||||
// Navigation labels
|
||||
NavLabelValueProps string
|
||||
NavLabelFeatures string
|
||||
NavLabelPricing string
|
||||
NavLabelBlog string
|
||||
NavLabelGallery string
|
||||
NavLabelCompare string
|
||||
NavLabelDocs string
|
||||
NavLabelReleases string
|
||||
NavLabelAPI string
|
||||
NavLabelIssues string
|
||||
}
|
||||
|
||||
// parseTranslationView parses a PagesTranslation into a flat view for the template
|
||||
func parseTranslationView(t *pages_model.Translation) *TranslationView {
|
||||
// overlayString extracts a string from a map
|
||||
func overlayString(m map[string]any, key string) string {
|
||||
if v, ok := m[key].(string); ok {
|
||||
return v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// overlayStringSlice extracts a []string-field slice from an overlay array of objects.
|
||||
// Each array element is a map; fieldKey is the key to extract from each map.
|
||||
func overlayStringSlice(overlay map[string]any, arrayKey, fieldKey string, padLen int) []string {
|
||||
result := make([]string, padLen)
|
||||
arr, ok := overlay[arrayKey].([]any)
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
for i, item := range arr {
|
||||
if i >= padLen {
|
||||
break
|
||||
}
|
||||
if m, ok := item.(map[string]any); ok {
|
||||
if v, ok := m[fieldKey].(string); ok {
|
||||
result[i] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// parseTranslationView parses a PagesTranslation into a flat view for the template.
|
||||
// config is used to determine array lengths for padding slices.
|
||||
func parseTranslationView(t *pages_model.Translation, config *pages_module.LandingConfig) *TranslationView {
|
||||
if t == nil || t.ConfigJSON == "" {
|
||||
return nil
|
||||
}
|
||||
@@ -738,37 +848,149 @@ func parseTranslationView(t *pages_model.Translation) *TranslationView {
|
||||
}
|
||||
|
||||
view := &TranslationView{}
|
||||
|
||||
// Brand
|
||||
if brand, ok := overlay["brand"].(map[string]any); ok {
|
||||
view.BrandName = overlayString(brand, "name")
|
||||
view.BrandTagline = overlayString(brand, "tagline")
|
||||
}
|
||||
|
||||
// Hero
|
||||
if hero, ok := overlay["hero"].(map[string]any); ok {
|
||||
if v, ok := hero["headline"].(string); ok {
|
||||
view.Headline = v
|
||||
}
|
||||
if v, ok := hero["subheadline"].(string); ok {
|
||||
view.Subheadline = v
|
||||
}
|
||||
view.Headline = overlayString(hero, "headline")
|
||||
view.Subheadline = overlayString(hero, "subheadline")
|
||||
if cta, ok := hero["primary_cta"].(map[string]any); ok {
|
||||
if v, ok := cta["label"].(string); ok {
|
||||
view.PrimaryCTA = v
|
||||
}
|
||||
view.PrimaryCTA = overlayString(cta, "label")
|
||||
}
|
||||
if cta, ok := hero["secondary_cta"].(map[string]any); ok {
|
||||
if v, ok := cta["label"].(string); ok {
|
||||
view.SecondaryCTA = v
|
||||
view.SecondaryCTA = overlayString(cta, "label")
|
||||
}
|
||||
}
|
||||
|
||||
// Stats
|
||||
view.StatsValues = overlayStringSlice(overlay, "stats", "value", len(config.Stats))
|
||||
view.StatsLabels = overlayStringSlice(overlay, "stats", "label", len(config.Stats))
|
||||
|
||||
// Value Props
|
||||
view.ValuePropTitles = overlayStringSlice(overlay, "value_props", "title", len(config.ValueProps))
|
||||
view.ValuePropDescs = overlayStringSlice(overlay, "value_props", "description", len(config.ValueProps))
|
||||
|
||||
// Features
|
||||
view.FeatureTitles = overlayStringSlice(overlay, "features", "title", len(config.Features))
|
||||
view.FeatureDescs = overlayStringSlice(overlay, "features", "description", len(config.Features))
|
||||
|
||||
// Testimonials (stored under social_proof.testimonials)
|
||||
view.TestimonialQuotes = make([]string, len(config.SocialProof.Testimonials))
|
||||
view.TestimonialRoles = make([]string, len(config.SocialProof.Testimonials))
|
||||
if sp, ok := overlay["social_proof"].(map[string]any); ok {
|
||||
if arr, ok := sp["testimonials"].([]any); ok {
|
||||
for i, item := range arr {
|
||||
if i >= len(config.SocialProof.Testimonials) {
|
||||
break
|
||||
}
|
||||
if m, ok := item.(map[string]any); ok {
|
||||
view.TestimonialQuotes[i] = overlayString(m, "quote")
|
||||
view.TestimonialRoles[i] = overlayString(m, "role")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pricing
|
||||
if pricing, ok := overlay["pricing"].(map[string]any); ok {
|
||||
view.PricingHeadline = overlayString(pricing, "headline")
|
||||
view.PricingSubheadline = overlayString(pricing, "subheadline")
|
||||
if plans, ok := pricing["plans"].([]any); ok {
|
||||
view.PlanNames = make([]string, len(config.Pricing.Plans))
|
||||
view.PlanPeriods = make([]string, len(config.Pricing.Plans))
|
||||
view.PlanCTAs = make([]string, len(config.Pricing.Plans))
|
||||
for i, item := range plans {
|
||||
if i >= len(config.Pricing.Plans) {
|
||||
break
|
||||
}
|
||||
if m, ok := item.(map[string]any); ok {
|
||||
view.PlanNames[i] = overlayString(m, "name")
|
||||
view.PlanPeriods[i] = overlayString(m, "period")
|
||||
view.PlanCTAs[i] = overlayString(m, "cta")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
view.PlanNames = make([]string, len(config.Pricing.Plans))
|
||||
view.PlanPeriods = make([]string, len(config.Pricing.Plans))
|
||||
view.PlanCTAs = make([]string, len(config.Pricing.Plans))
|
||||
}
|
||||
} else {
|
||||
view.PlanNames = make([]string, len(config.Pricing.Plans))
|
||||
view.PlanPeriods = make([]string, len(config.Pricing.Plans))
|
||||
view.PlanCTAs = make([]string, len(config.Pricing.Plans))
|
||||
}
|
||||
|
||||
// CTA Section
|
||||
if ctaSec, ok := overlay["cta_section"].(map[string]any); ok {
|
||||
if v, ok := ctaSec["headline"].(string); ok {
|
||||
view.CTAHeadline = v
|
||||
}
|
||||
if v, ok := ctaSec["subheadline"].(string); ok {
|
||||
view.CTASubheadline = v
|
||||
}
|
||||
view.CTAHeadline = overlayString(ctaSec, "headline")
|
||||
view.CTASubheadline = overlayString(ctaSec, "subheadline")
|
||||
if btn, ok := ctaSec["button"].(map[string]any); ok {
|
||||
if v, ok := btn["label"].(string); ok {
|
||||
view.CTAButton = v
|
||||
view.CTAButton = overlayString(btn, "label")
|
||||
}
|
||||
}
|
||||
|
||||
// Blog
|
||||
if blog, ok := overlay["blog"].(map[string]any); ok {
|
||||
view.BlogHeadline = overlayString(blog, "headline")
|
||||
view.BlogSubheadline = overlayString(blog, "subheadline")
|
||||
if btn, ok := blog["cta_button"].(map[string]any); ok {
|
||||
view.BlogCTAButton = overlayString(btn, "label")
|
||||
}
|
||||
}
|
||||
|
||||
// Gallery
|
||||
if gallery, ok := overlay["gallery"].(map[string]any); ok {
|
||||
view.GalleryHeadline = overlayString(gallery, "headline")
|
||||
view.GallerySubheadline = overlayString(gallery, "subheadline")
|
||||
}
|
||||
|
||||
// Comparison
|
||||
if comp, ok := overlay["comparison"].(map[string]any); ok {
|
||||
view.ComparisonHeadline = overlayString(comp, "headline")
|
||||
view.ComparisonSubheadline = overlayString(comp, "subheadline")
|
||||
}
|
||||
|
||||
// Footer
|
||||
view.FooterLinkLabels = make([]string, len(config.Footer.Links))
|
||||
if footer, ok := overlay["footer"].(map[string]any); ok {
|
||||
view.FooterCopyright = overlayString(footer, "copyright")
|
||||
if links, ok := footer["links"].([]any); ok {
|
||||
for i, item := range links {
|
||||
if i >= len(config.Footer.Links) {
|
||||
break
|
||||
}
|
||||
if m, ok := item.(map[string]any); ok {
|
||||
view.FooterLinkLabels[i] = overlayString(m, "label")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SEO
|
||||
if seo, ok := overlay["seo"].(map[string]any); ok {
|
||||
view.SEOTitle = overlayString(seo, "title")
|
||||
view.SEODescription = overlayString(seo, "description")
|
||||
}
|
||||
|
||||
// Navigation labels
|
||||
if nav, ok := overlay["navigation"].(map[string]any); ok {
|
||||
view.NavLabelValueProps = overlayString(nav, "label_value_props")
|
||||
view.NavLabelFeatures = overlayString(nav, "label_features")
|
||||
view.NavLabelPricing = overlayString(nav, "label_pricing")
|
||||
view.NavLabelBlog = overlayString(nav, "label_blog")
|
||||
view.NavLabelGallery = overlayString(nav, "label_gallery")
|
||||
view.NavLabelCompare = overlayString(nav, "label_compare")
|
||||
view.NavLabelDocs = overlayString(nav, "label_docs")
|
||||
view.NavLabelReleases = overlayString(nav, "label_releases")
|
||||
view.NavLabelAPI = overlayString(nav, "label_api")
|
||||
view.NavLabelIssues = overlayString(nav, "label_issues")
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
@@ -776,6 +998,19 @@ func parseTranslationView(t *pages_model.Translation) *TranslationView {
|
||||
func buildTranslationJSON(ctx *context.Context) string {
|
||||
overlay := map[string]any{}
|
||||
|
||||
// Brand
|
||||
brand := map[string]any{}
|
||||
if v := ctx.FormString("trans_brand_name"); v != "" {
|
||||
brand["name"] = v
|
||||
}
|
||||
if v := ctx.FormString("trans_brand_tagline"); v != "" {
|
||||
brand["tagline"] = v
|
||||
}
|
||||
if len(brand) > 0 {
|
||||
overlay["brand"] = brand
|
||||
}
|
||||
|
||||
// Hero
|
||||
hero := map[string]any{}
|
||||
if v := ctx.FormString("trans_headline"); v != "" {
|
||||
hero["headline"] = v
|
||||
@@ -793,6 +1028,103 @@ func buildTranslationJSON(ctx *context.Context) string {
|
||||
overlay["hero"] = hero
|
||||
}
|
||||
|
||||
// Stats (indexed)
|
||||
var stats []map[string]any
|
||||
for i := range 20 {
|
||||
v := ctx.FormString(fmt.Sprintf("trans_stat_%d_value", i))
|
||||
l := ctx.FormString(fmt.Sprintf("trans_stat_%d_label", i))
|
||||
if v == "" && l == "" {
|
||||
// Check if there are more by looking ahead
|
||||
if ctx.FormString(fmt.Sprintf("trans_stat_%d_value", i+1)) == "" &&
|
||||
ctx.FormString(fmt.Sprintf("trans_stat_%d_label", i+1)) == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
stats = append(stats, map[string]any{"value": v, "label": l})
|
||||
}
|
||||
if len(stats) > 0 {
|
||||
overlay["stats"] = stats
|
||||
}
|
||||
|
||||
// Value Props (indexed)
|
||||
var valueProps []map[string]any
|
||||
for i := range 20 {
|
||||
t := ctx.FormString(fmt.Sprintf("trans_vp_%d_title", i))
|
||||
d := ctx.FormString(fmt.Sprintf("trans_vp_%d_desc", i))
|
||||
if t == "" && d == "" {
|
||||
if ctx.FormString(fmt.Sprintf("trans_vp_%d_title", i+1)) == "" &&
|
||||
ctx.FormString(fmt.Sprintf("trans_vp_%d_desc", i+1)) == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
valueProps = append(valueProps, map[string]any{"title": t, "description": d})
|
||||
}
|
||||
if len(valueProps) > 0 {
|
||||
overlay["value_props"] = valueProps
|
||||
}
|
||||
|
||||
// Features (indexed)
|
||||
var features []map[string]any
|
||||
for i := range 20 {
|
||||
t := ctx.FormString(fmt.Sprintf("trans_feat_%d_title", i))
|
||||
d := ctx.FormString(fmt.Sprintf("trans_feat_%d_desc", i))
|
||||
if t == "" && d == "" {
|
||||
if ctx.FormString(fmt.Sprintf("trans_feat_%d_title", i+1)) == "" &&
|
||||
ctx.FormString(fmt.Sprintf("trans_feat_%d_desc", i+1)) == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
features = append(features, map[string]any{"title": t, "description": d})
|
||||
}
|
||||
if len(features) > 0 {
|
||||
overlay["features"] = features
|
||||
}
|
||||
|
||||
// Testimonials (indexed)
|
||||
var testimonials []map[string]any
|
||||
for i := range 20 {
|
||||
q := ctx.FormString(fmt.Sprintf("trans_test_%d_quote", i))
|
||||
r := ctx.FormString(fmt.Sprintf("trans_test_%d_role", i))
|
||||
if q == "" && r == "" {
|
||||
if ctx.FormString(fmt.Sprintf("trans_test_%d_quote", i+1)) == "" &&
|
||||
ctx.FormString(fmt.Sprintf("trans_test_%d_role", i+1)) == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
testimonials = append(testimonials, map[string]any{"quote": q, "role": r})
|
||||
}
|
||||
if len(testimonials) > 0 {
|
||||
overlay["social_proof"] = map[string]any{"testimonials": testimonials}
|
||||
}
|
||||
|
||||
// Pricing
|
||||
pricing := map[string]any{}
|
||||
if v := ctx.FormString("trans_pricing_headline"); v != "" {
|
||||
pricing["headline"] = v
|
||||
}
|
||||
if v := ctx.FormString("trans_pricing_subheadline"); v != "" {
|
||||
pricing["subheadline"] = v
|
||||
}
|
||||
var plans []map[string]any
|
||||
for i := range 10 {
|
||||
n := ctx.FormString(fmt.Sprintf("trans_plan_%d_name", i))
|
||||
p := ctx.FormString(fmt.Sprintf("trans_plan_%d_period", i))
|
||||
c := ctx.FormString(fmt.Sprintf("trans_plan_%d_cta", i))
|
||||
if n == "" && p == "" && c == "" {
|
||||
if ctx.FormString(fmt.Sprintf("trans_plan_%d_name", i+1)) == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
plans = append(plans, map[string]any{"name": n, "period": p, "cta": c})
|
||||
}
|
||||
if len(plans) > 0 {
|
||||
pricing["plans"] = plans
|
||||
}
|
||||
if len(pricing) > 0 {
|
||||
overlay["pricing"] = pricing
|
||||
}
|
||||
|
||||
// CTA Section
|
||||
ctaSec := map[string]any{}
|
||||
if v := ctx.FormString("trans_cta_headline"); v != "" {
|
||||
ctaSec["headline"] = v
|
||||
@@ -807,6 +1139,94 @@ func buildTranslationJSON(ctx *context.Context) string {
|
||||
overlay["cta_section"] = ctaSec
|
||||
}
|
||||
|
||||
// Blog
|
||||
blog := map[string]any{}
|
||||
if v := ctx.FormString("trans_blog_headline"); v != "" {
|
||||
blog["headline"] = v
|
||||
}
|
||||
if v := ctx.FormString("trans_blog_subheadline"); v != "" {
|
||||
blog["subheadline"] = v
|
||||
}
|
||||
if v := ctx.FormString("trans_blog_cta"); v != "" {
|
||||
blog["cta_button"] = map[string]any{"label": v}
|
||||
}
|
||||
if len(blog) > 0 {
|
||||
overlay["blog"] = blog
|
||||
}
|
||||
|
||||
// Gallery
|
||||
gallery := map[string]any{}
|
||||
if v := ctx.FormString("trans_gallery_headline"); v != "" {
|
||||
gallery["headline"] = v
|
||||
}
|
||||
if v := ctx.FormString("trans_gallery_subheadline"); v != "" {
|
||||
gallery["subheadline"] = v
|
||||
}
|
||||
if len(gallery) > 0 {
|
||||
overlay["gallery"] = gallery
|
||||
}
|
||||
|
||||
// Comparison
|
||||
comp := map[string]any{}
|
||||
if v := ctx.FormString("trans_comparison_headline"); v != "" {
|
||||
comp["headline"] = v
|
||||
}
|
||||
if v := ctx.FormString("trans_comparison_subheadline"); v != "" {
|
||||
comp["subheadline"] = v
|
||||
}
|
||||
if len(comp) > 0 {
|
||||
overlay["comparison"] = comp
|
||||
}
|
||||
|
||||
// Footer
|
||||
footer := map[string]any{}
|
||||
if v := ctx.FormString("trans_footer_copyright"); v != "" {
|
||||
footer["copyright"] = v
|
||||
}
|
||||
var footerLinks []map[string]any
|
||||
for i := range 20 {
|
||||
l := ctx.FormString(fmt.Sprintf("trans_footer_link_%d", i))
|
||||
if l == "" {
|
||||
if ctx.FormString(fmt.Sprintf("trans_footer_link_%d", i+1)) == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
footerLinks = append(footerLinks, map[string]any{"label": l})
|
||||
}
|
||||
if len(footerLinks) > 0 {
|
||||
footer["links"] = footerLinks
|
||||
}
|
||||
if len(footer) > 0 {
|
||||
overlay["footer"] = footer
|
||||
}
|
||||
|
||||
// SEO
|
||||
seo := map[string]any{}
|
||||
if v := ctx.FormString("trans_seo_title"); v != "" {
|
||||
seo["title"] = v
|
||||
}
|
||||
if v := ctx.FormString("trans_seo_description"); v != "" {
|
||||
seo["description"] = v
|
||||
}
|
||||
if len(seo) > 0 {
|
||||
overlay["seo"] = seo
|
||||
}
|
||||
|
||||
// Navigation labels
|
||||
nav := map[string]any{}
|
||||
for _, key := range []string{
|
||||
"label_value_props", "label_features", "label_pricing",
|
||||
"label_blog", "label_gallery", "label_compare",
|
||||
"label_docs", "label_releases", "label_api", "label_issues",
|
||||
} {
|
||||
if v := ctx.FormString("trans_nav_" + key); v != "" {
|
||||
nav[key] = v
|
||||
}
|
||||
}
|
||||
if len(nav) > 0 {
|
||||
overlay["navigation"] = nav
|
||||
}
|
||||
|
||||
if len(overlay) == 0 {
|
||||
return ""
|
||||
}
|
||||
@@ -823,18 +1243,19 @@ func PagesLanguages(ctx *context.Context) {
|
||||
config := getPagesLandingConfig(ctx)
|
||||
ctx.Data["LanguageNames"] = pages_module.LanguageDisplayNames()
|
||||
|
||||
// Build a function to check if a language is enabled
|
||||
enabledLangs := config.I18n.Languages
|
||||
ctx.Data["IsLangEnabled"] = func(code string) bool {
|
||||
return slices.Contains(enabledLangs, code)
|
||||
// Build a map to check if a language is enabled
|
||||
enabledLangsMap := make(map[string]bool)
|
||||
for _, code := range config.I18n.Languages {
|
||||
enabledLangsMap[code] = true
|
||||
}
|
||||
ctx.Data["EnabledLangs"] = enabledLangsMap
|
||||
|
||||
// Load translations into a map[lang]*TranslationView
|
||||
translationMap := make(map[string]*TranslationView)
|
||||
translations, err := pages_model.GetTranslationsByRepoID(ctx, ctx.Repo.Repository.ID)
|
||||
if err == nil {
|
||||
for _, t := range translations {
|
||||
translationMap[t.Lang] = parseTranslationView(t)
|
||||
translationMap[t.Lang] = parseTranslationView(t, config)
|
||||
}
|
||||
}
|
||||
ctx.Data["TranslationMap"] = translationMap
|
||||
@@ -953,6 +1374,73 @@ func PagesLanguagesPost(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.pages.ai_translate_success"))
|
||||
|
||||
case "ai_translate_all":
|
||||
defaultLang := config.I18n.DefaultLang
|
||||
if defaultLang == "" {
|
||||
defaultLang = "en"
|
||||
}
|
||||
var (
|
||||
mu sync.Mutex
|
||||
successCount int
|
||||
failCount int
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
repoID := ctx.Repo.Repository.ID
|
||||
repo := ctx.Repo.Repository
|
||||
for _, lang := range config.I18n.Languages {
|
||||
if lang == defaultLang {
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(lang string) {
|
||||
defer wg.Done()
|
||||
translated, err := pages_service.TranslateLandingPageContent(ctx, repo, config, lang)
|
||||
if err != nil {
|
||||
log.Error("AI translation failed for %s: %v", lang, err)
|
||||
mu.Lock()
|
||||
failCount++
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
existing, err := pages_model.GetTranslation(ctx, repoID, lang)
|
||||
if err != nil {
|
||||
log.Error("GetTranslation failed for %s: %v", lang, err)
|
||||
failCount++
|
||||
return
|
||||
}
|
||||
if existing != nil {
|
||||
existing.ConfigJSON = translated
|
||||
existing.AutoGenerated = true
|
||||
if err := pages_model.UpdateTranslation(ctx, existing); err != nil {
|
||||
log.Error("UpdateTranslation failed for %s: %v", lang, err)
|
||||
failCount++
|
||||
return
|
||||
}
|
||||
} else {
|
||||
t := &pages_model.Translation{
|
||||
RepoID: repoID,
|
||||
Lang: lang,
|
||||
ConfigJSON: translated,
|
||||
AutoGenerated: true,
|
||||
}
|
||||
if err := pages_model.CreateTranslation(ctx, t); err != nil {
|
||||
log.Error("CreateTranslation failed for %s: %v", lang, err)
|
||||
failCount++
|
||||
return
|
||||
}
|
||||
}
|
||||
successCount++
|
||||
}(lang)
|
||||
}
|
||||
wg.Wait()
|
||||
if failCount == 0 {
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.pages.ai_translate_all_success", successCount))
|
||||
} else {
|
||||
ctx.Flash.Warning(ctx.Tr("repo.settings.pages.ai_translate_all_partial", successCount, successCount+failCount, failCount))
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Redirect(ctx.Repo.Repository.Link() + "/settings/pages/languages")
|
||||
@@ -992,3 +1480,64 @@ func loadRawReadme(ctx *context.Context, repo *repo_model.Repository) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// PagesAdvanced renders the advanced settings page
|
||||
func PagesAdvanced(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.settings.pages.advanced")
|
||||
ctx.Data["PageIsSettingsPages"] = true
|
||||
ctx.Data["PageIsSettingsPagesAdvanced"] = true
|
||||
setCommonPagesData(ctx)
|
||||
ctx.HTML(http.StatusOK, tplRepoSettingsPagesAdvanced)
|
||||
}
|
||||
|
||||
// PagesAdvancedPost handles the advanced settings form submission
|
||||
func PagesAdvancedPost(ctx *context.Context) {
|
||||
config := getPagesLandingConfig(ctx)
|
||||
|
||||
// Parse static routes
|
||||
var routes []string
|
||||
for i := range 100 {
|
||||
route := strings.TrimSpace(ctx.FormString(fmt.Sprintf("static_route_%d", i)))
|
||||
if route != "" {
|
||||
routes = append(routes, route)
|
||||
}
|
||||
}
|
||||
config.Advanced.StaticRoutes = routes
|
||||
|
||||
// Parse redirects
|
||||
redirects := make(map[string]string)
|
||||
for i := range 100 {
|
||||
from := strings.TrimSpace(ctx.FormString(fmt.Sprintf("redirect_from_%d", i)))
|
||||
to := strings.TrimSpace(ctx.FormString(fmt.Sprintf("redirect_to_%d", i)))
|
||||
if from != "" && to != "" {
|
||||
redirects[from] = to
|
||||
}
|
||||
}
|
||||
// Also handle redirects keyed by path (from existing entries)
|
||||
for key, vals := range ctx.Req.Form {
|
||||
if strings.HasPrefix(key, "redirect_from_/") {
|
||||
from := strings.TrimSpace(vals[0])
|
||||
toKey := "redirect_to_" + strings.TrimPrefix(key, "redirect_from_")
|
||||
to := strings.TrimSpace(ctx.Req.FormValue(toKey))
|
||||
if from != "" && to != "" {
|
||||
redirects[from] = to
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(redirects) > 0 {
|
||||
config.Advanced.Redirects = redirects
|
||||
} else {
|
||||
config.Advanced.Redirects = nil
|
||||
}
|
||||
|
||||
// Parse remaining fields
|
||||
config.Advanced.CustomCSS = ctx.FormString("custom_css")
|
||||
config.Advanced.CustomHead = ctx.FormString("custom_head")
|
||||
|
||||
if err := savePagesLandingConfig(ctx, config); err != nil {
|
||||
ctx.ServerError("SavePagesConfig", err)
|
||||
return
|
||||
}
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.pages.saved"))
|
||||
ctx.Redirect(ctx.Repo.Repository.Link() + "/settings/pages/advanced")
|
||||
}
|
||||
|
||||
@@ -1347,6 +1347,7 @@ func registerWebRoutes(m *web.Router) {
|
||||
m.Combo("/footer").Get(repo_setting.PagesFooter).Post(repo_setting.PagesFooterPost)
|
||||
m.Combo("/theme").Get(repo_setting.PagesTheme).Post(repo_setting.PagesThemePost)
|
||||
m.Combo("/languages").Get(repo_setting.PagesLanguages).Post(repo_setting.PagesLanguagesPost)
|
||||
m.Combo("/advanced").Get(repo_setting.PagesAdvanced).Post(repo_setting.PagesAdvancedPost)
|
||||
})
|
||||
m.Group("/actions/general", func() {
|
||||
m.Get("", repo_setting.ActionsGeneralSettings)
|
||||
|
||||
@@ -219,25 +219,160 @@ func TranslateLandingPageContent(ctx context.Context, repo *repo_model.Repositor
|
||||
|
||||
// buildTranslatableContent extracts translatable text from a config for the AI
|
||||
func buildTranslatableContent(config *pages_module.LandingConfig) string {
|
||||
data, _ := json.Marshal(map[string]any{
|
||||
"hero": map[string]any{
|
||||
"headline": config.Hero.Headline,
|
||||
"subheadline": config.Hero.Subheadline,
|
||||
"primary_cta": map[string]string{"label": config.Hero.PrimaryCTA.Label},
|
||||
"secondary_cta": map[string]string{"label": config.Hero.SecondaryCTA.Label},
|
||||
},
|
||||
"stats": config.Stats,
|
||||
"value_props": config.ValueProps,
|
||||
"features": config.Features,
|
||||
"cta_section": map[string]any{
|
||||
"headline": config.CTASection.Headline,
|
||||
"subheadline": config.CTASection.Subheadline,
|
||||
"button": map[string]string{"label": config.CTASection.Button.Label},
|
||||
},
|
||||
"seo": map[string]any{
|
||||
content := map[string]any{}
|
||||
|
||||
// Brand
|
||||
if config.Brand.Name != "" || config.Brand.Tagline != "" {
|
||||
content["brand"] = map[string]any{
|
||||
"name": config.Brand.Name,
|
||||
"tagline": config.Brand.Tagline,
|
||||
}
|
||||
}
|
||||
|
||||
// Hero
|
||||
content["hero"] = map[string]any{
|
||||
"headline": config.Hero.Headline,
|
||||
"subheadline": config.Hero.Subheadline,
|
||||
"primary_cta": map[string]string{"label": config.Hero.PrimaryCTA.Label},
|
||||
"secondary_cta": map[string]string{"label": config.Hero.SecondaryCTA.Label},
|
||||
}
|
||||
|
||||
// Stats, Value Props, Features (already struct-serializable)
|
||||
if len(config.Stats) > 0 {
|
||||
content["stats"] = config.Stats
|
||||
}
|
||||
if len(config.ValueProps) > 0 {
|
||||
content["value_props"] = config.ValueProps
|
||||
}
|
||||
if len(config.Features) > 0 {
|
||||
content["features"] = config.Features
|
||||
}
|
||||
|
||||
// Testimonials
|
||||
if len(config.SocialProof.Testimonials) > 0 {
|
||||
testimonials := make([]map[string]string, 0, len(config.SocialProof.Testimonials))
|
||||
for _, t := range config.SocialProof.Testimonials {
|
||||
testimonials = append(testimonials, map[string]string{
|
||||
"quote": t.Quote,
|
||||
"role": t.Role,
|
||||
})
|
||||
}
|
||||
content["social_proof"] = map[string]any{"testimonials": testimonials}
|
||||
}
|
||||
|
||||
// Pricing
|
||||
if config.Pricing.Headline != "" || len(config.Pricing.Plans) > 0 {
|
||||
pricing := map[string]any{
|
||||
"headline": config.Pricing.Headline,
|
||||
"subheadline": config.Pricing.Subheadline,
|
||||
}
|
||||
if len(config.Pricing.Plans) > 0 {
|
||||
plans := make([]map[string]string, 0, len(config.Pricing.Plans))
|
||||
for _, p := range config.Pricing.Plans {
|
||||
plans = append(plans, map[string]string{
|
||||
"name": p.Name,
|
||||
"period": p.Period,
|
||||
"cta": p.CTA,
|
||||
})
|
||||
}
|
||||
pricing["plans"] = plans
|
||||
}
|
||||
content["pricing"] = pricing
|
||||
}
|
||||
|
||||
// CTA Section
|
||||
content["cta_section"] = map[string]any{
|
||||
"headline": config.CTASection.Headline,
|
||||
"subheadline": config.CTASection.Subheadline,
|
||||
"button": map[string]string{"label": config.CTASection.Button.Label},
|
||||
}
|
||||
|
||||
// Blog
|
||||
if config.Blog.Enabled {
|
||||
blogHeadline := config.Blog.Headline
|
||||
if blogHeadline == "" {
|
||||
blogHeadline = "Latest Posts"
|
||||
}
|
||||
blog := map[string]any{
|
||||
"headline": blogHeadline,
|
||||
"subheadline": config.Blog.Subheadline,
|
||||
}
|
||||
if config.Blog.CTAButton.Label != "" {
|
||||
blog["cta_button"] = map[string]string{"label": config.Blog.CTAButton.Label}
|
||||
}
|
||||
content["blog"] = blog
|
||||
}
|
||||
|
||||
// Gallery
|
||||
if config.Gallery.Enabled {
|
||||
galleryHeadline := config.Gallery.Headline
|
||||
if galleryHeadline == "" {
|
||||
galleryHeadline = "Gallery"
|
||||
}
|
||||
content["gallery"] = map[string]any{
|
||||
"headline": galleryHeadline,
|
||||
"subheadline": config.Gallery.Subheadline,
|
||||
}
|
||||
}
|
||||
|
||||
// Comparison
|
||||
if config.Comparison.Enabled {
|
||||
compHeadline := config.Comparison.Headline
|
||||
if compHeadline == "" {
|
||||
compHeadline = "How We Compare"
|
||||
}
|
||||
content["comparison"] = map[string]any{
|
||||
"headline": compHeadline,
|
||||
"subheadline": config.Comparison.Subheadline,
|
||||
}
|
||||
}
|
||||
|
||||
// Footer
|
||||
if config.Footer.Copyright != "" || len(config.Footer.Links) > 0 {
|
||||
footer := map[string]any{
|
||||
"copyright": config.Footer.Copyright,
|
||||
}
|
||||
if len(config.Footer.Links) > 0 {
|
||||
links := make([]map[string]string, 0, len(config.Footer.Links))
|
||||
for _, l := range config.Footer.Links {
|
||||
links = append(links, map[string]string{"label": l.Label})
|
||||
}
|
||||
footer["links"] = links
|
||||
}
|
||||
content["footer"] = footer
|
||||
}
|
||||
|
||||
// SEO
|
||||
if config.SEO.Title != "" || config.SEO.Description != "" {
|
||||
content["seo"] = map[string]any{
|
||||
"title": config.SEO.Title,
|
||||
"description": config.SEO.Description,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Navigation labels (for translating nav items and section headers)
|
||||
// Use template-specific defaults so AI translates the correct terms
|
||||
// (e.g. "Systems Analysis" for architecture-deep-dive, not generic "Value Props")
|
||||
defaults := pages_module.TemplateDefaultLabels(config.Template)
|
||||
labelOrDefault := func(label, def string) string {
|
||||
if label != "" {
|
||||
return label
|
||||
}
|
||||
return def
|
||||
}
|
||||
content["navigation"] = map[string]any{
|
||||
"label_value_props": labelOrDefault(config.Navigation.LabelValueProps, defaults.LabelValueProps),
|
||||
"label_features": labelOrDefault(config.Navigation.LabelFeatures, defaults.LabelFeatures),
|
||||
"label_pricing": labelOrDefault(config.Navigation.LabelPricing, defaults.LabelPricing),
|
||||
"label_blog": labelOrDefault(config.Navigation.LabelBlog, defaults.LabelBlog),
|
||||
"label_gallery": labelOrDefault(config.Navigation.LabelGallery, defaults.LabelGallery),
|
||||
"label_compare": labelOrDefault(config.Navigation.LabelCompare, defaults.LabelCompare),
|
||||
"label_docs": labelOrDefault(config.Navigation.LabelDocs, "Docs"),
|
||||
"label_releases": labelOrDefault(config.Navigation.LabelReleases, "Releases"),
|
||||
"label_api": labelOrDefault(config.Navigation.LabelAPI, "API"),
|
||||
"label_issues": labelOrDefault(config.Navigation.LabelIssues, "Issues"),
|
||||
}
|
||||
|
||||
data, _ := json.Marshal(content)
|
||||
return string(data)
|
||||
}
|
||||
|
||||
@@ -1006,16 +1006,16 @@
|
||||
{{range .Config.Footer.Links}}
|
||||
<a href="{{.URL}}" class="ad-nav-link">{{.Label}}</a>
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="ad-nav-link">Docs</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="ad-nav-link">API</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="ad-nav-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="ad-nav-link">Issues</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="ad-nav-link">Value Props</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="ad-nav-link">Features</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="ad-nav-link">Pricing</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="ad-nav-link">Blog</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="ad-nav-link">Gallery</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="ad-nav-link">Compare</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="ad-nav-link">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="ad-nav-link">{{if .Config.Navigation.LabelAPI}}{{.Config.Navigation.LabelAPI}}{{else}}API{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="ad-nav-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="ad-nav-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="ad-nav-link">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Value Props{{end}}</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="ad-nav-link">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="ad-nav-link">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="ad-nav-link">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="ad-nav-link">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="ad-nav-link">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}
|
||||
<a href="{{.RepoURL}}" class="ad-nav-cta">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
@@ -1071,7 +1071,7 @@
|
||||
<section class="ad-features" style="padding-top: 120px;">
|
||||
<div class="ad-features-inner">
|
||||
<div class="ad-section-header ad-reveal">
|
||||
<div class="ad-section-label">Blog</div>
|
||||
<div class="ad-section-label">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}All Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1102,7 +1102,7 @@
|
||||
</section>
|
||||
{{else}}
|
||||
<!-- Hero Section -->
|
||||
<section class="ad-hero">
|
||||
<section class="ad-hero" id="hero">
|
||||
<div class="ad-hero-content">
|
||||
<div class="ad-badge ad-reveal visible">
|
||||
<span class="ad-badge-dot"></span>
|
||||
@@ -1123,11 +1123,6 @@
|
||||
{{.Config.Hero.PrimaryCTA.Label}}
|
||||
{{svg "octicon-arrow-right" 16}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="{{.RepoURL}}" class="ad-btn-primary" data-cta="primary">
|
||||
Get Started
|
||||
{{svg "octicon-arrow-right" 16}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
{{if .Config.Hero.SecondaryCTA.Label}}
|
||||
@@ -1135,14 +1130,26 @@
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
{{.Config.Hero.SecondaryCTA.Label}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="{{.RepoURL}}" class="ad-btn-secondary" data-cta="secondary">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
View Source
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; margin-top: 16px;" class="ad-reveal visible ad-reveal-delay-4">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="ad-download-item" style="display: inline-flex; align-items: center; gap: 10px; clip-path: none;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="ad-download-item" style="display: inline-flex; align-items: center; gap: 10px; clip-path: none;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .Config.Hero.CodeExample}}
|
||||
<div class="ad-code-block ad-reveal visible ad-reveal-delay-4">
|
||||
<span class="ad-code-prompt">$</span>
|
||||
@@ -1157,7 +1164,7 @@
|
||||
|
||||
<!-- Stats Section -->
|
||||
{{if or .Config.Stats (gt .NumStars 0)}}
|
||||
<section class="ad-stats">
|
||||
<section class="ad-stats" id="stats">
|
||||
<div class="ad-stats-inner ad-reveal">
|
||||
{{if .Config.Stats}}
|
||||
{{range .Config.Stats}}
|
||||
@@ -1188,7 +1195,7 @@
|
||||
|
||||
<!-- Downloads Section -->
|
||||
{{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}}
|
||||
<section class="ad-downloads">
|
||||
<section class="ad-downloads" id="downloads">
|
||||
<div class="ad-downloads-inner ad-reveal">
|
||||
<h2>Download v{{.LatestReleaseTag}}</h2>
|
||||
<p>Get the latest release</p>
|
||||
@@ -1251,25 +1258,6 @@
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 13px; color: var(--ad-muted); font-family: 'Barlow Condensed', sans-serif; letter-spacing: 0.12em; text-transform: uppercase;">{{svg "octicon-device-mobile" 16}} App Stores</h4>
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="ad-download-item" style="display: inline-flex; align-items: center; gap: 10px; clip-path: none;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="ad-download-item" style="display: inline-flex; align-items: center; gap: 10px; clip-path: none;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if $otherFiles}}
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 13px; color: var(--ad-muted); font-family: 'Barlow Condensed', sans-serif; letter-spacing: 0.12em; text-transform: uppercase;">{{svg "octicon-file" 16}} Other</h4>
|
||||
@@ -1287,10 +1275,9 @@
|
||||
<section class="ad-features" id="value-props">
|
||||
<div class="ad-features-inner">
|
||||
<div class="ad-section-header ad-reveal">
|
||||
<div class="ad-section-label">Systems Analysis</div>
|
||||
<h2>{{if .Config.Brand.Name}}Why {{.Config.Brand.Name}}?{{else}}Why Choose Us{{end}}</h2>
|
||||
<p>Everything you need to get started quickly.</p>
|
||||
</div>
|
||||
<div class="ad-section-label">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Systems Analysis{{end}}</div>
|
||||
<h2>{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}{{if .Config.Brand.Name}}Why {{.Config.Brand.Name}}?{{else}}Why Choose Us{{end}}{{end}}</h2>
|
||||
</div>
|
||||
<div class="ad-features-grid">
|
||||
{{range .Config.ValueProps}}
|
||||
<div class="ad-feature-card ad-reveal">
|
||||
@@ -1311,10 +1298,9 @@
|
||||
<section class="ad-features" id="features" style="padding-top: 40px;">
|
||||
<div class="ad-features-inner">
|
||||
<div class="ad-section-header ad-reveal">
|
||||
<div class="ad-section-label">Technical Specifications</div>
|
||||
<h2>Features</h2>
|
||||
<p>Powerful capabilities at your fingertips.</p>
|
||||
</div>
|
||||
<div class="ad-section-label">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Technical Specifications{{end}}</div>
|
||||
<h2>{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</h2>
|
||||
</div>
|
||||
<div class="ad-features-grid">
|
||||
{{range .Config.Features}}
|
||||
<div class="ad-feature-card ad-reveal">
|
||||
@@ -1332,7 +1318,7 @@
|
||||
|
||||
<!-- Social Proof -->
|
||||
{{if or .Config.SocialProof.Logos .Config.SocialProof.Testimonials}}
|
||||
<section class="ad-social-proof">
|
||||
<section class="ad-social-proof" id="social-proof">
|
||||
<div class="ad-social-proof-inner">
|
||||
{{if .Config.SocialProof.Logos}}
|
||||
<div class="ad-logos ad-reveal">
|
||||
@@ -1372,7 +1358,7 @@
|
||||
<section class="ad-pricing" id="pricing">
|
||||
<div class="ad-pricing-inner">
|
||||
<div class="ad-section-header ad-reveal">
|
||||
<div class="ad-section-label">Resource Allocation</div>
|
||||
<div class="ad-section-label">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Resource Allocation{{end}}</div>
|
||||
<h2>{{if .Config.Pricing.Headline}}{{.Config.Pricing.Headline}}{{else}}Pricing{{end}}</h2>
|
||||
<p>{{if .Config.Pricing.Subheadline}}{{.Config.Pricing.Subheadline}}{{else}}Choose the plan that works for you{{end}}</p>
|
||||
</div>
|
||||
@@ -1398,7 +1384,7 @@
|
||||
|
||||
<!-- CTA Section -->
|
||||
{{if .Config.CTASection.Headline}}
|
||||
<section class="ad-cta-section">
|
||||
<section class="ad-cta-section" id="cta">
|
||||
<div class="ad-cta-inner ad-reveal">
|
||||
<h2>{{.Config.CTASection.Headline}}</h2>
|
||||
{{if .Config.CTASection.Subheadline}}
|
||||
@@ -1417,7 +1403,7 @@
|
||||
<section class="ad-features" id="blog" style="border-top: 1px solid var(--ad-dim);">
|
||||
<div class="ad-features-inner">
|
||||
<div class="ad-section-header ad-reveal">
|
||||
<div class="ad-section-label">Dispatches</div>
|
||||
<div class="ad-section-label">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Dispatches{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}Latest Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1456,7 +1442,7 @@
|
||||
<section class="ad-features" id="gallery" style="border-top: 1px solid var(--ad-dim);">
|
||||
<div class="ad-features-inner">
|
||||
<div class="ad-section-header ad-reveal">
|
||||
<div class="ad-section-label">Visual Index</div>
|
||||
<div class="ad-section-label">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Visual Index{{end}}</div>
|
||||
<h2>{{if .Config.Gallery.Headline}}{{.Config.Gallery.Headline}}{{else}}Gallery{{end}}</h2>
|
||||
{{if .Config.Gallery.Subheadline}}<p>{{.Config.Gallery.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1481,7 +1467,7 @@
|
||||
<section class="ad-features" id="comparison" style="border-top: 1px solid var(--ad-dim);">
|
||||
<div class="ad-features-inner">
|
||||
<div class="ad-section-header ad-reveal">
|
||||
<div class="ad-section-label">Compare</div>
|
||||
<div class="ad-section-label">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</div>
|
||||
<h2>{{if .Config.Comparison.Headline}}{{.Config.Comparison.Headline}}{{else}}How We Compare{{end}}</h2>
|
||||
{{if .Config.Comparison.Subheadline}}<p>{{.Config.Comparison.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1564,8 +1550,8 @@
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}<a href="{{.RepoURL}}" class="ad-footer-link">Repository</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="ad-footer-link">Documentation</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="ad-footer-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="ad-footer-link">Issues</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="ad-footer-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="ad-footer-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
.nb-reveal-delay-1 { transition-delay: 0.1s; }
|
||||
.nb-reveal-delay-2 { transition-delay: 0.2s; }
|
||||
.nb-reveal-delay-3 { transition-delay: 0.3s; }
|
||||
.nb-reveal-delay-4 { transition-delay: 0.4s; }
|
||||
|
||||
.nb-page {
|
||||
min-height: 100vh;
|
||||
@@ -1128,16 +1129,16 @@
|
||||
{{range .Config.Footer.Links}}
|
||||
<a href="{{.URL}}" class="nb-nav-link">{{.Label}}</a>
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="nb-nav-link">Docs</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="nb-nav-link">API</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="nb-nav-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="nb-nav-link">Issues</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="nb-nav-link">Why</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="nb-nav-link">Features</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="nb-nav-link">Pricing</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="nb-nav-link">Blog</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="nb-nav-link">Gallery</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="nb-nav-link">Compare</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="nb-nav-link">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="nb-nav-link">{{if .Config.Navigation.LabelAPI}}{{.Config.Navigation.LabelAPI}}{{else}}API{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="nb-nav-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="nb-nav-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="nb-nav-link">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Why{{end}}</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="nb-nav-link">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="nb-nav-link">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="nb-nav-link">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="nb-nav-link">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="nb-nav-link">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}
|
||||
<a href="{{.RepoURL}}" class="nb-nav-repo">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
@@ -1168,16 +1169,16 @@
|
||||
{{range .Config.Footer.Links}}
|
||||
<a href="{{.URL}}" class="nb-nav-link">{{.Label}}</a>
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="nb-nav-link">Docs</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="nb-nav-link">API</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="nb-nav-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="nb-nav-link">Issues</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="nb-nav-link">Why</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="nb-nav-link">Features</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="nb-nav-link">Pricing</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="nb-nav-link">Blog</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="nb-nav-link">Gallery</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="nb-nav-link">Compare</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="nb-nav-link">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="nb-nav-link">{{if .Config.Navigation.LabelAPI}}{{.Config.Navigation.LabelAPI}}{{else}}API{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="nb-nav-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="nb-nav-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="nb-nav-link">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Why{{end}}</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="nb-nav-link">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="nb-nav-link">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="nb-nav-link">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="nb-nav-link">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="nb-nav-link">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}
|
||||
<a href="{{.RepoURL}}" class="nb-nav-repo">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
@@ -1217,7 +1218,7 @@
|
||||
<section class="nb-features" style="padding-top: 120px;">
|
||||
<div class="nb-features-inner">
|
||||
<div class="nb-section-header nb-reveal">
|
||||
<div class="nb-section-label">Blog</div>
|
||||
<div class="nb-section-label">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}All Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1248,7 +1249,7 @@
|
||||
</section>
|
||||
{{else}}
|
||||
<!-- Hero Section -->
|
||||
<section class="nb-hero">
|
||||
<section class="nb-hero" id="hero">
|
||||
<div class="nb-hero-accent nb-hero-accent-1"></div>
|
||||
<div class="nb-hero-accent nb-hero-accent-2"></div>
|
||||
<div class="nb-hero-accent nb-hero-accent-3">*</div>
|
||||
@@ -1273,12 +1274,29 @@
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; margin-top: 16px;" class="nb-reveal nb-reveal-delay-4">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="nb-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="nb-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{if .Config.Stats}}
|
||||
<!-- Stats Section -->
|
||||
<section class="nb-stats">
|
||||
<section class="nb-stats" id="stats">
|
||||
<div class="nb-stats-grid">
|
||||
{{range .Config.Stats}}
|
||||
<div class="nb-stat-card nb-reveal">
|
||||
@@ -1291,7 +1309,7 @@
|
||||
{{end}}
|
||||
|
||||
{{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}}
|
||||
<section class="nb-downloads">
|
||||
<section class="nb-downloads" id="downloads">
|
||||
<div class="nb-downloads-inner">
|
||||
<div class="nb-downloads-header nb-reveal">
|
||||
<div class="nb-section-label">Download</div>
|
||||
@@ -1357,25 +1375,6 @@
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 13px; color: var(--nb-muted); text-transform: uppercase; letter-spacing: 0.1em;">{{svg "octicon-device-mobile" 16}} App Stores</h4>
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="nb-download-item nb-reveal" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="nb-download-item nb-reveal" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if $otherFiles}}
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 13px; color: var(--nb-muted); text-transform: uppercase; letter-spacing: 0.1em;">{{svg "octicon-file" 16}} Other</h4>
|
||||
@@ -1390,7 +1389,7 @@
|
||||
|
||||
{{if .Config.SocialProof.Logos}}
|
||||
<!-- Trust Bar -->
|
||||
<section class="nb-trust-bar">
|
||||
<section class="nb-trust-bar" id="social-proof">
|
||||
<p class="nb-trust-label nb-reveal">Trusted by the best</p>
|
||||
<div class="nb-trust-logos nb-reveal nb-reveal-delay-1">
|
||||
{{range .Config.SocialProof.Logos}}
|
||||
@@ -1404,10 +1403,9 @@
|
||||
<!-- Value Props Section -->
|
||||
<section class="nb-value-props" id="value-props">
|
||||
<div class="nb-section-header nb-reveal">
|
||||
<div class="nb-section-label">Why choose this</div>
|
||||
<div class="nb-section-label">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Why choose this{{end}}</div>
|
||||
<h2>Unlock your <span class="nb-glow-primary">potential</span></h2>
|
||||
<p>Everything you need to create without limits</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nb-value-grid">
|
||||
{{range .Config.ValueProps}}
|
||||
<div class="nb-value-tile nb-reveal">
|
||||
@@ -1426,7 +1424,7 @@
|
||||
<!-- Features Section -->
|
||||
<section class="nb-features" id="features">
|
||||
<div class="nb-section-header nb-reveal">
|
||||
<div class="nb-section-label">Capabilities</div>
|
||||
<div class="nb-section-label">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Capabilities{{end}}</div>
|
||||
<h2>Packed with <span class="nb-glow-text">power</span></h2>
|
||||
</div>
|
||||
<div class="nb-feature-list">
|
||||
@@ -1447,7 +1445,7 @@
|
||||
<!-- Pricing Section -->
|
||||
<section class="nb-pricing" id="pricing">
|
||||
<div class="nb-section-header nb-reveal">
|
||||
<div class="nb-section-label">Investment</div>
|
||||
<div class="nb-section-label">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Investment{{end}}</div>
|
||||
<h2>{{if .Config.Pricing.Headline}}{{.Config.Pricing.Headline}}{{else}}Simple <span class="nb-glow-text">Pricing</span>{{end}}</h2>
|
||||
<p>Choose the plan that works for you</p>
|
||||
</div>
|
||||
@@ -1474,7 +1472,7 @@
|
||||
|
||||
<!-- Testimonials Section -->
|
||||
{{if .Config.SocialProof.Testimonials}}
|
||||
<section class="nb-testimonial-section">
|
||||
<section class="nb-testimonial-section" id="testimonials">
|
||||
<div class="nb-testimonial-block nb-reveal">
|
||||
<span class="nb-quote-mark">“</span>
|
||||
<div class="nb-testimonials-container">
|
||||
@@ -1508,7 +1506,7 @@
|
||||
|
||||
{{if .Config.CTASection.Headline}}
|
||||
<!-- CTA Banner -->
|
||||
<section class="nb-cta-section">
|
||||
<section class="nb-cta-section" id="cta">
|
||||
<div class="nb-cta-banner nb-reveal">
|
||||
<h2>{{.Config.CTASection.Headline}}</h2>
|
||||
{{if .Config.CTASection.Subheadline}}
|
||||
@@ -1528,7 +1526,7 @@
|
||||
{{if and .Config.Blog.Enabled .BlogPosts}}
|
||||
<section class="nb-features" id="blog" style="border-top: 1px solid var(--nb-border-hard);">
|
||||
<div class="nb-section-header nb-reveal">
|
||||
<div class="nb-section-label">Blog</div>
|
||||
<div class="nb-section-label">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}<span class="nb-glow-text">{{.Config.Blog.Headline}}</span>{{else}}Latest <span class="nb-glow-text">Posts</span>{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1566,7 +1564,7 @@
|
||||
{{if and .Config.Gallery.Enabled .GalleryImages}}
|
||||
<section class="nb-features" id="gallery" style="border-top: 1px solid var(--nb-border-hard);">
|
||||
<div class="nb-section-header nb-reveal">
|
||||
<div class="nb-section-label">Gallery</div>
|
||||
<div class="nb-section-label">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</div>
|
||||
<h2><span class="nb-glow-text">{{if .Config.Gallery.Headline}}{{.Config.Gallery.Headline}}{{else}}Gallery{{end}}</span></h2>
|
||||
{{if .Config.Gallery.Subheadline}}<p>{{.Config.Gallery.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1590,7 +1588,7 @@
|
||||
<section class="nb-features" id="comparison" style="border-top: 1px solid var(--nb-border);">
|
||||
<div class="nb-features-inner">
|
||||
<div class="nb-section-header nb-reveal">
|
||||
<div class="nb-section-label">Compare</div>
|
||||
<div class="nb-section-label">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</div>
|
||||
<h2>{{if .Config.Comparison.Headline}}{{.Config.Comparison.Headline}}{{else}}How We Compare{{end}}</h2>
|
||||
{{if .Config.Comparison.Subheadline}}<p>{{.Config.Comparison.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1676,9 +1674,9 @@
|
||||
<a href="{{.URL}}" class="nb-footer-link">{{.Label}}</a>
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}<a href="{{.RepoURL}}" class="nb-footer-link">Repository</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="nb-footer-link">Docs</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="nb-footer-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="nb-footer-link">Issues</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="nb-footer-link">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="nb-footer-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="nb-footer-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -1012,16 +1012,16 @@
|
||||
{{range .Config.Footer.Links}}
|
||||
<a href="{{.URL}}" class="ct-nav-link">{{.Label}}</a>
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="ct-nav-link">Docs</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="ct-nav-link">API</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="ct-nav-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="ct-nav-link">Issues</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="ct-nav-link">Value Props</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="ct-nav-link">Features</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="ct-nav-link">Pricing</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="ct-nav-link">Blog</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="ct-nav-link">Gallery</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="ct-nav-link">Compare</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="ct-nav-link">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="ct-nav-link">{{if .Config.Navigation.LabelAPI}}{{.Config.Navigation.LabelAPI}}{{else}}API{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="ct-nav-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="ct-nav-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="ct-nav-link">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Value Props{{end}}</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="ct-nav-link">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="ct-nav-link">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="ct-nav-link">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="ct-nav-link">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="ct-nav-link">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}
|
||||
<a href="{{.RepoURL}}" class="ct-nav-cta">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="14" height="14" alt="GitCaddy">
|
||||
@@ -1077,7 +1077,7 @@
|
||||
<section class="ct-features" style="padding-top: 120px;">
|
||||
<div class="ct-features-inner">
|
||||
<div class="ct-section-header ct-reveal">
|
||||
<div class="ct-section-label">Blog</div>
|
||||
<div class="ct-section-label">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}All Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1108,7 +1108,7 @@
|
||||
</section>
|
||||
{{else}}
|
||||
<!-- Hero Section -->
|
||||
<section class="ct-hero">
|
||||
<section class="ct-hero" id="hero">
|
||||
<div class="ct-hero-glow"></div>
|
||||
<div class="ct-hero-vignette"></div>
|
||||
|
||||
@@ -1130,11 +1130,6 @@
|
||||
{{.Config.Hero.PrimaryCTA.Label}}
|
||||
{{svg "octicon-arrow-right" 16}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="{{.RepoURL}}" class="ct-btn-primary" data-cta="primary">
|
||||
Get Started
|
||||
{{svg "octicon-arrow-right" 16}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
{{if .Config.Hero.SecondaryCTA.Label}}
|
||||
@@ -1142,14 +1137,26 @@
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
{{.Config.Hero.SecondaryCTA.Label}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="{{.RepoURL}}" class="ct-btn-secondary" data-cta="secondary">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
View Source
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; margin-top: 16px;" class="ct-reveal visible ct-reveal-delay-4">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="ct-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="ct-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .Config.Hero.CodeExample}}
|
||||
<div class="ct-code-block ct-reveal visible ct-reveal-delay-4">
|
||||
<div class="ct-code-titlebar">
|
||||
@@ -1172,7 +1179,7 @@
|
||||
|
||||
<!-- Stats Section -->
|
||||
{{if or .Config.Stats (gt .NumStars 0)}}
|
||||
<section class="ct-stats">
|
||||
<section class="ct-stats" id="stats">
|
||||
<div class="ct-stats-inner ct-reveal">
|
||||
{{if .Config.Stats}}
|
||||
{{range .Config.Stats}}
|
||||
@@ -1203,7 +1210,7 @@
|
||||
|
||||
<!-- Downloads Section -->
|
||||
{{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}}
|
||||
<section class="ct-downloads">
|
||||
<section class="ct-downloads" id="downloads">
|
||||
<div class="ct-downloads-inner ct-reveal">
|
||||
<h2>Download v{{.LatestReleaseTag}}</h2>
|
||||
<p>Get the latest release</p>
|
||||
@@ -1266,25 +1273,6 @@
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 12px; color: var(--ct-muted); letter-spacing: 0.08em; text-transform: uppercase;">{{svg "octicon-device-mobile" 16}} App Stores</h4>
|
||||
<div style="display: flex; gap: 8px; flex-wrap: wrap;">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="ct-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="ct-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if $otherFiles}}
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 12px; color: var(--ct-muted); letter-spacing: 0.08em; text-transform: uppercase;">{{svg "octicon-file" 16}} Other</h4>
|
||||
@@ -1302,10 +1290,9 @@
|
||||
<section class="ct-features" id="value-props">
|
||||
<div class="ct-features-inner">
|
||||
<div class="ct-section-header ct-reveal">
|
||||
<div class="ct-section-label">Why choose us</div>
|
||||
<h2>{{if .Config.Brand.Name}}Why {{.Config.Brand.Name}}?{{else}}Why Choose Us{{end}}</h2>
|
||||
<p>Everything you need to get started quickly.</p>
|
||||
</div>
|
||||
<div class="ct-section-label">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Why choose us{{end}}</div>
|
||||
<h2>{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}{{if .Config.Brand.Name}}Why {{.Config.Brand.Name}}?{{else}}Why Choose Us{{end}}{{end}}</h2>
|
||||
</div>
|
||||
<div class="ct-features-grid">
|
||||
{{range .Config.ValueProps}}
|
||||
<div class="ct-feature-card ct-reveal">
|
||||
@@ -1326,10 +1313,9 @@
|
||||
<section class="ct-features" id="features" style="padding-top: 40px;">
|
||||
<div class="ct-features-inner">
|
||||
<div class="ct-section-header ct-reveal">
|
||||
<div class="ct-section-label">Capabilities</div>
|
||||
<h2>Features</h2>
|
||||
<p>Powerful capabilities at your fingertips.</p>
|
||||
</div>
|
||||
<div class="ct-section-label">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Capabilities{{end}}</div>
|
||||
<h2>{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</h2>
|
||||
</div>
|
||||
<div class="ct-features-grid">
|
||||
{{range .Config.Features}}
|
||||
<div class="ct-feature-card ct-reveal">
|
||||
@@ -1347,7 +1333,7 @@
|
||||
|
||||
<!-- Social Proof -->
|
||||
{{if or .Config.SocialProof.Logos .Config.SocialProof.Testimonials}}
|
||||
<section class="ct-social-proof">
|
||||
<section class="ct-social-proof" id="social-proof">
|
||||
<div class="ct-social-proof-inner">
|
||||
{{if .Config.SocialProof.Logos}}
|
||||
<div class="ct-logos ct-reveal">
|
||||
@@ -1387,7 +1373,7 @@
|
||||
<section class="ct-pricing" id="pricing">
|
||||
<div class="ct-pricing-inner">
|
||||
<div class="ct-section-header ct-reveal">
|
||||
<div class="ct-section-label">Pricing</div>
|
||||
<div class="ct-section-label">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</div>
|
||||
<h2>{{if .Config.Pricing.Headline}}{{.Config.Pricing.Headline}}{{else}}Pricing{{end}}</h2>
|
||||
<p>{{if .Config.Pricing.Subheadline}}{{.Config.Pricing.Subheadline}}{{else}}Choose the plan that works for you{{end}}</p>
|
||||
</div>
|
||||
@@ -1413,7 +1399,7 @@
|
||||
|
||||
<!-- CTA Section -->
|
||||
{{if .Config.CTASection.Headline}}
|
||||
<section class="ct-cta-section">
|
||||
<section class="ct-cta-section" id="cta">
|
||||
<div class="ct-cta-inner ct-reveal">
|
||||
<h2>{{.Config.CTASection.Headline}}</h2>
|
||||
{{if .Config.CTASection.Subheadline}}
|
||||
@@ -1432,7 +1418,7 @@
|
||||
<section class="ct-features" id="blog" style="border-top: 1px solid var(--ct-dim);">
|
||||
<div class="ct-features-inner">
|
||||
<div class="ct-section-header ct-reveal">
|
||||
<div class="ct-section-label">Blog</div>
|
||||
<div class="ct-section-label">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}Latest Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1471,7 +1457,7 @@
|
||||
<section class="ct-features" id="gallery" style="border-top: 1px solid var(--ct-dim);">
|
||||
<div class="ct-features-inner">
|
||||
<div class="ct-section-header ct-reveal">
|
||||
<div class="ct-section-label">Gallery</div>
|
||||
<div class="ct-section-label">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</div>
|
||||
<h2>{{if .Config.Gallery.Headline}}{{.Config.Gallery.Headline}}{{else}}Gallery{{end}}</h2>
|
||||
{{if .Config.Gallery.Subheadline}}<p>{{.Config.Gallery.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1496,7 +1482,7 @@
|
||||
<section class="ct-features" id="comparison" style="border-top: 1px solid var(--ct-dim);">
|
||||
<div class="ct-features-inner">
|
||||
<div class="ct-section-header ct-reveal">
|
||||
<div class="ct-section-label">Compare</div>
|
||||
<div class="ct-section-label">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</div>
|
||||
<h2>{{if .Config.Comparison.Headline}}{{.Config.Comparison.Headline}}{{else}}How We Compare{{end}}</h2>
|
||||
{{if .Config.Comparison.Subheadline}}<p>{{.Config.Comparison.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1579,8 +1565,8 @@
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}<a href="{{.RepoURL}}" class="ct-footer-link">Repository</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="ct-footer-link">Documentation</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="ct-footer-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="ct-footer-link">Issues</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="ct-footer-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="ct-footer-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -895,16 +895,16 @@
|
||||
{{range .Config.Footer.Links}}
|
||||
<a href="{{.URL}}" class="dt-nav-link">{{.Label}}</a>
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="dt-nav-link">Docs</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="dt-nav-link">API</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="dt-nav-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="dt-nav-link">Issues</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="dt-nav-link">Value Props</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="dt-nav-link">Features</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="dt-nav-link">Pricing</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="dt-nav-link">Blog</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="dt-nav-link">Gallery</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="dt-nav-link">Compare</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="dt-nav-link">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="dt-nav-link">{{if .Config.Navigation.LabelAPI}}{{.Config.Navigation.LabelAPI}}{{else}}API{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="dt-nav-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="dt-nav-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="dt-nav-link">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Value Props{{end}}</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="dt-nav-link">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="dt-nav-link">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="dt-nav-link">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="dt-nav-link">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="dt-nav-link">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}
|
||||
<a href="{{.RepoURL}}" class="dt-nav-cta">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
@@ -960,7 +960,7 @@
|
||||
<section class="dt-features" style="padding-top: 100px;">
|
||||
<div class="dt-features-inner">
|
||||
<div class="dt-section-header dt-reveal">
|
||||
<div class="dt-section-label">Blog</div>
|
||||
<div class="dt-section-label">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}All Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -991,7 +991,7 @@
|
||||
</section>
|
||||
{{else}}
|
||||
<!-- Hero Section -->
|
||||
<section class="dt-hero">
|
||||
<section class="dt-hero" id="hero">
|
||||
<div class="dt-hero-content">
|
||||
<div class="dt-badge dt-reveal visible">
|
||||
<span class="dt-badge-label">status</span>
|
||||
@@ -1011,11 +1011,6 @@
|
||||
{{.Config.Hero.PrimaryCTA.Label}}
|
||||
{{svg "octicon-arrow-right" 16}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="{{.RepoURL}}" class="dt-btn-primary" data-cta="primary">
|
||||
Get Started
|
||||
{{svg "octicon-arrow-right" 16}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
{{if .Config.Hero.SecondaryCTA.Label}}
|
||||
@@ -1023,14 +1018,26 @@
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
{{.Config.Hero.SecondaryCTA.Label}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="{{.RepoURL}}" class="dt-btn-secondary" data-cta="secondary">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
View Source
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; margin-top: 16px;" class="dt-reveal visible dt-reveal-delay-4">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="dt-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="dt-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .Config.Hero.CodeExample}}
|
||||
<div class="dt-code-block dt-reveal visible dt-reveal-delay-4">
|
||||
<span class="dt-code-prompt">$</span>
|
||||
@@ -1045,7 +1052,7 @@
|
||||
|
||||
<!-- Stats Section -->
|
||||
{{if or .Config.Stats (gt .NumStars 0)}}
|
||||
<section class="dt-stats">
|
||||
<section class="dt-stats" id="stats">
|
||||
<div class="dt-stats-inner dt-reveal">
|
||||
{{if .Config.Stats}}
|
||||
{{range .Config.Stats}}
|
||||
@@ -1076,7 +1083,7 @@
|
||||
|
||||
<!-- Downloads Section -->
|
||||
{{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}}
|
||||
<section class="dt-downloads">
|
||||
<section class="dt-downloads" id="downloads">
|
||||
<div class="dt-downloads-inner dt-reveal">
|
||||
<h2>Download v{{.LatestReleaseTag}}</h2>
|
||||
<p>Get the latest release</p>
|
||||
@@ -1139,25 +1146,6 @@
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 12px; color: var(--dt-muted); font-family: 'JetBrains Mono', monospace; letter-spacing: 0.08em; text-transform: uppercase;">{{svg "octicon-device-mobile" 16}} App Stores</h4>
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="dt-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="dt-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if $otherFiles}}
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 12px; color: var(--dt-muted); font-family: 'JetBrains Mono', monospace; letter-spacing: 0.08em; text-transform: uppercase;">{{svg "octicon-file" 16}} Other</h4>
|
||||
@@ -1175,10 +1163,9 @@
|
||||
<section class="dt-features" id="value-props">
|
||||
<div class="dt-features-inner">
|
||||
<div class="dt-section-header dt-reveal">
|
||||
<div class="dt-section-label">Why choose us</div>
|
||||
<h2>{{if .Config.Brand.Name}}Why {{.Config.Brand.Name}}?{{else}}Why Choose Us{{end}}</h2>
|
||||
<p>Everything you need to get started quickly.</p>
|
||||
</div>
|
||||
<div class="dt-section-label">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Why choose us{{end}}</div>
|
||||
<h2>{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}{{if .Config.Brand.Name}}Why {{.Config.Brand.Name}}?{{else}}Why Choose Us{{end}}{{end}}</h2>
|
||||
</div>
|
||||
<div class="dt-features-grid">
|
||||
{{range .Config.ValueProps}}
|
||||
<div class="dt-feature-card dt-reveal">
|
||||
@@ -1199,10 +1186,9 @@
|
||||
<section class="dt-features" id="features" style="padding-top: 40px;">
|
||||
<div class="dt-features-inner">
|
||||
<div class="dt-section-header dt-reveal">
|
||||
<div class="dt-section-label">Capabilities</div>
|
||||
<h2>Features</h2>
|
||||
<p>Powerful capabilities at your fingertips.</p>
|
||||
</div>
|
||||
<div class="dt-section-label">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Capabilities{{end}}</div>
|
||||
<h2>{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</h2>
|
||||
</div>
|
||||
<div class="dt-features-grid">
|
||||
{{range .Config.Features}}
|
||||
<div class="dt-feature-card dt-reveal">
|
||||
@@ -1220,7 +1206,7 @@
|
||||
|
||||
<!-- Social Proof -->
|
||||
{{if or .Config.SocialProof.Logos .Config.SocialProof.Testimonials}}
|
||||
<section class="dt-social-proof">
|
||||
<section class="dt-social-proof" id="social-proof">
|
||||
<div class="dt-social-proof-inner">
|
||||
{{if .Config.SocialProof.Logos}}
|
||||
<div class="dt-logos dt-reveal">
|
||||
@@ -1260,7 +1246,7 @@
|
||||
<section class="dt-pricing" id="pricing">
|
||||
<div class="dt-pricing-inner">
|
||||
<div class="dt-section-header dt-reveal">
|
||||
<div class="dt-section-label">Pricing</div>
|
||||
<div class="dt-section-label">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</div>
|
||||
<h2>{{if .Config.Pricing.Headline}}{{.Config.Pricing.Headline}}{{else}}Pricing{{end}}</h2>
|
||||
<p>{{if .Config.Pricing.Subheadline}}{{.Config.Pricing.Subheadline}}{{else}}Choose the plan that works for you{{end}}</p>
|
||||
</div>
|
||||
@@ -1286,7 +1272,7 @@
|
||||
|
||||
<!-- CTA Section -->
|
||||
{{if .Config.CTASection.Headline}}
|
||||
<section class="dt-cta-section">
|
||||
<section class="dt-cta-section" id="cta">
|
||||
<div class="dt-cta-inner dt-reveal">
|
||||
<h2>{{.Config.CTASection.Headline}}</h2>
|
||||
{{if .Config.CTASection.Subheadline}}
|
||||
@@ -1305,7 +1291,7 @@
|
||||
<section class="dt-features" id="blog" style="border-top: 1px solid var(--dt-dim);">
|
||||
<div class="dt-features-inner">
|
||||
<div class="dt-section-header dt-reveal">
|
||||
<div class="dt-section-label">Blog</div>
|
||||
<div class="dt-section-label">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}Latest Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1344,7 +1330,7 @@
|
||||
<section class="dt-features" id="gallery" style="border-top: 1px solid var(--dt-dim);">
|
||||
<div class="dt-features-inner">
|
||||
<div class="dt-section-header dt-reveal">
|
||||
<div class="dt-section-label">Gallery</div>
|
||||
<div class="dt-section-label">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</div>
|
||||
<h2>{{if .Config.Gallery.Headline}}{{.Config.Gallery.Headline}}{{else}}Gallery{{end}}</h2>
|
||||
{{if .Config.Gallery.Subheadline}}<p>{{.Config.Gallery.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1369,7 +1355,7 @@
|
||||
<section class="dt-features" id="comparison" style="border-top: 1px solid var(--dt-dim);">
|
||||
<div class="dt-features-inner">
|
||||
<div class="dt-section-header dt-reveal">
|
||||
<div class="dt-section-label">Compare</div>
|
||||
<div class="dt-section-label">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</div>
|
||||
<h2>{{if .Config.Comparison.Headline}}{{.Config.Comparison.Headline}}{{else}}How We Compare{{end}}</h2>
|
||||
{{if .Config.Comparison.Subheadline}}<p>{{.Config.Comparison.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1452,8 +1438,8 @@
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}<a href="{{.RepoURL}}" class="dt-footer-link">Repository</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="dt-footer-link">Documentation</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="dt-footer-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="dt-footer-link">Issues</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="dt-footer-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="dt-footer-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -920,16 +920,16 @@
|
||||
{{range .Config.Footer.Links}}
|
||||
<a href="{{.URL}}" class="df-nav-link">{{.Label}}</a>
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="df-nav-link">Docs</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="df-nav-link">API</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="df-nav-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="df-nav-link">Issues</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="df-nav-link">Value Props</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="df-nav-link">Features</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="df-nav-link">Pricing</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="df-nav-link">Blog</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="df-nav-link">Gallery</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="df-nav-link">Compare</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="df-nav-link">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="df-nav-link">{{if .Config.Navigation.LabelAPI}}{{.Config.Navigation.LabelAPI}}{{else}}API{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="df-nav-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="df-nav-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="df-nav-link">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Value Props{{end}}</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="df-nav-link">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="df-nav-link">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="df-nav-link">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="df-nav-link">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="df-nav-link">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}
|
||||
<a href="{{.RepoURL}}" class="df-nav-cta">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
@@ -985,7 +985,7 @@
|
||||
<section class="df-features" style="padding-top: 120px;">
|
||||
<div class="df-features-inner">
|
||||
<div class="df-section-header df-reveal">
|
||||
<div class="df-section-label">Blog</div>
|
||||
<div class="df-section-label">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}All Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1016,7 +1016,7 @@
|
||||
</section>
|
||||
{{else}}
|
||||
<!-- Hero Section -->
|
||||
<section class="df-hero">
|
||||
<section class="df-hero" id="hero">
|
||||
<div class="df-hero-content">
|
||||
<div class="df-badge df-reveal visible">
|
||||
<span class="df-badge-dot"></span>
|
||||
@@ -1035,11 +1035,6 @@
|
||||
{{.Config.Hero.PrimaryCTA.Label}}
|
||||
{{svg "octicon-arrow-right" 16}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="{{.RepoURL}}" class="df-btn-primary" data-cta="primary">
|
||||
Get Started
|
||||
{{svg "octicon-arrow-right" 16}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
{{if .Config.Hero.SecondaryCTA.Label}}
|
||||
@@ -1047,14 +1042,26 @@
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
{{.Config.Hero.SecondaryCTA.Label}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="{{.RepoURL}}" class="df-btn-secondary" data-cta="secondary">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
View Source
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; margin-top: 16px;" class="df-reveal visible df-reveal-delay-4">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="df-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="df-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .Config.Hero.CodeExample}}
|
||||
<div class="df-code-block df-reveal visible df-reveal-delay-4">
|
||||
<span class="df-code-prompt">$</span>
|
||||
@@ -1069,7 +1076,7 @@
|
||||
|
||||
<!-- Stats Section -->
|
||||
{{if or .Config.Stats (gt .NumStars 0)}}
|
||||
<section class="df-stats">
|
||||
<section class="df-stats" id="stats">
|
||||
<div class="df-stats-inner df-reveal">
|
||||
{{if .Config.Stats}}
|
||||
{{range .Config.Stats}}
|
||||
@@ -1100,7 +1107,7 @@
|
||||
|
||||
<!-- Downloads Section -->
|
||||
{{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}}
|
||||
<section class="df-downloads">
|
||||
<section class="df-downloads" id="downloads">
|
||||
<div class="df-downloads-inner df-reveal">
|
||||
<h2>Download v{{.LatestReleaseTag}}</h2>
|
||||
<p>Get the latest release</p>
|
||||
@@ -1163,25 +1170,6 @@
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 13px; color: var(--df-muted); letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700;">{{svg "octicon-device-mobile" 16}} App Stores</h4>
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="df-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="df-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if $otherFiles}}
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 13px; color: var(--df-muted); letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700;">{{svg "octicon-file" 16}} Other</h4>
|
||||
@@ -1199,10 +1187,9 @@
|
||||
<section class="df-features" id="value-props">
|
||||
<div class="df-features-inner">
|
||||
<div class="df-section-header df-reveal">
|
||||
<div class="df-section-label">Why choose us</div>
|
||||
<h2>{{if .Config.Brand.Name}}Why {{.Config.Brand.Name}}?{{else}}Why Choose Us{{end}}</h2>
|
||||
<p>Everything you need to get started quickly.</p>
|
||||
</div>
|
||||
<div class="df-section-label">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Why choose us{{end}}</div>
|
||||
<h2>{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}{{if .Config.Brand.Name}}Why {{.Config.Brand.Name}}?{{else}}Why Choose Us{{end}}{{end}}</h2>
|
||||
</div>
|
||||
<div class="df-features-grid">
|
||||
{{range .Config.ValueProps}}
|
||||
<div class="df-feature-card df-reveal">
|
||||
@@ -1223,10 +1210,9 @@
|
||||
<section class="df-features" id="features" style="padding-top: 40px;">
|
||||
<div class="df-features-inner">
|
||||
<div class="df-section-header df-reveal">
|
||||
<div class="df-section-label">Capabilities</div>
|
||||
<h2>Features</h2>
|
||||
<p>Powerful capabilities at your fingertips.</p>
|
||||
</div>
|
||||
<div class="df-section-label">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Capabilities{{end}}</div>
|
||||
<h2>{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</h2>
|
||||
</div>
|
||||
<div class="df-features-grid">
|
||||
{{range .Config.Features}}
|
||||
<div class="df-feature-card df-reveal">
|
||||
@@ -1244,7 +1230,7 @@
|
||||
|
||||
<!-- Social Proof -->
|
||||
{{if or .Config.SocialProof.Logos .Config.SocialProof.Testimonials}}
|
||||
<section class="df-social-proof">
|
||||
<section class="df-social-proof" id="social-proof">
|
||||
<div class="df-social-proof-inner">
|
||||
{{if .Config.SocialProof.Logos}}
|
||||
<div class="df-logos df-reveal">
|
||||
@@ -1284,7 +1270,7 @@
|
||||
<section class="df-pricing" id="pricing">
|
||||
<div class="df-pricing-inner">
|
||||
<div class="df-section-header df-reveal">
|
||||
<div class="df-section-label">Pricing</div>
|
||||
<div class="df-section-label">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</div>
|
||||
<h2>{{if .Config.Pricing.Headline}}{{.Config.Pricing.Headline}}{{else}}Pricing{{end}}</h2>
|
||||
<p>{{if .Config.Pricing.Subheadline}}{{.Config.Pricing.Subheadline}}{{else}}Choose the plan that works for you{{end}}</p>
|
||||
</div>
|
||||
@@ -1310,7 +1296,7 @@
|
||||
|
||||
<!-- CTA Section -->
|
||||
{{if .Config.CTASection.Headline}}
|
||||
<section class="df-cta-section">
|
||||
<section class="df-cta-section" id="cta">
|
||||
<div class="df-cta-inner df-reveal">
|
||||
<h2>{{.Config.CTASection.Headline}}</h2>
|
||||
{{if .Config.CTASection.Subheadline}}
|
||||
@@ -1329,7 +1315,7 @@
|
||||
<section class="df-features" id="blog" style="border-top: 1px solid var(--df-border);">
|
||||
<div class="df-features-inner">
|
||||
<div class="df-section-header df-reveal">
|
||||
<div class="df-section-label">Blog</div>
|
||||
<div class="df-section-label">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}Latest Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1368,7 +1354,7 @@
|
||||
<section class="df-features" id="gallery" style="border-top: 1px solid var(--df-border);">
|
||||
<div class="df-features-inner">
|
||||
<div class="df-section-header df-reveal">
|
||||
<div class="df-section-label">Gallery</div>
|
||||
<div class="df-section-label">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</div>
|
||||
<h2>{{if .Config.Gallery.Headline}}{{.Config.Gallery.Headline}}{{else}}Gallery{{end}}</h2>
|
||||
{{if .Config.Gallery.Subheadline}}<p>{{.Config.Gallery.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1393,7 +1379,7 @@
|
||||
<section class="df-features" id="comparison" style="border-top: 1px solid var(--df-border);">
|
||||
<div class="df-features-inner">
|
||||
<div class="df-section-header df-reveal">
|
||||
<div class="df-section-label">Compare</div>
|
||||
<div class="df-section-label">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</div>
|
||||
<h2>{{if .Config.Comparison.Headline}}{{.Config.Comparison.Headline}}{{else}}How We Compare{{end}}</h2>
|
||||
{{if .Config.Comparison.Subheadline}}<p>{{.Config.Comparison.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1476,8 +1462,8 @@
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}<a href="{{.RepoURL}}" class="df-footer-link">Repository</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="df-footer-link">Documentation</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="df-footer-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="df-footer-link">Issues</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="df-footer-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="df-footer-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
<div class="pages-footer-column">
|
||||
<ul class="pages-footer-list">
|
||||
{{if .Config.Navigation.ShowRepository}}<li><a href="{{AppSubUrl}}/{{.Repository.FullName}}">Repository</a></li>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<li><a href="{{AppSubUrl}}/{{.Repository.FullName}}/wiki">Documentation</a></li>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<li><a href="{{AppSubUrl}}/{{.Repository.FullName}}/releases">Releases</a></li>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<li><a href="{{AppSubUrl}}/{{.Repository.FullName}}/issues">Issues</a></li>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<li><a href="{{AppSubUrl}}/{{.Repository.FullName}}/wiki">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Documentation{{end}}</a></li>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<li><a href="{{AppSubUrl}}/{{.Repository.FullName}}/releases">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a></li>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<li><a href="{{AppSubUrl}}/{{.Repository.FullName}}/issues">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a></li>{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
{{range .Config.Footer.Links}}
|
||||
<a href="{{.URL}}" class="pages-nav-link">{{.Label}}</a>
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{AppSubUrl}}/{{.Repository.FullName}}/wiki" class="pages-nav-link">Docs</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{AppSubUrl}}/{{.Repository.FullName}}/swagger" class="pages-nav-link">API</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{AppSubUrl}}/{{.Repository.FullName}}/releases" class="pages-nav-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{AppSubUrl}}/{{.Repository.FullName}}/issues" class="pages-nav-link">Issues</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{AppSubUrl}}/{{.Repository.FullName}}/wiki" class="pages-nav-link">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{AppSubUrl}}/{{.Repository.FullName}}/swagger" class="pages-nav-link">{{if .Config.Navigation.LabelAPI}}{{.Config.Navigation.LabelAPI}}{{else}}API{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{AppSubUrl}}/{{.Repository.FullName}}/releases" class="pages-nav-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{AppSubUrl}}/{{.Repository.FullName}}/issues" class="pages-nav-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}
|
||||
<a href="{{AppSubUrl}}/{{.Repository.FullName}}" class="ui mini button" target="_blank">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy"> View Source
|
||||
|
||||
@@ -995,16 +995,16 @@
|
||||
{{range .Config.Footer.Links}}
|
||||
<a href="{{.URL}}" class="ea-nav-link">{{.Label}}</a>
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="ea-nav-link">Docs</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="ea-nav-link">API</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="ea-nav-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="ea-nav-link">Issues</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#why" class="ea-nav-link">Why</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="ea-nav-link">Features</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="ea-nav-link">Pricing</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="ea-nav-link">Blog</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="ea-nav-link">Gallery</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="ea-nav-link">Compare</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="ea-nav-link">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="ea-nav-link">{{if .Config.Navigation.LabelAPI}}{{.Config.Navigation.LabelAPI}}{{else}}API{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="ea-nav-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="ea-nav-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#why" class="ea-nav-link">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Why{{end}}</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="ea-nav-link">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="ea-nav-link">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="ea-nav-link">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="ea-nav-link">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="ea-nav-link">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}
|
||||
<a href="{{.RepoURL}}" class="ea-btn-text">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
@@ -1030,16 +1030,16 @@
|
||||
{{range .Config.Footer.Links}}
|
||||
<a href="{{.URL}}" class="ea-nav-link">{{.Label}}</a>
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="ea-nav-link">Docs</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="ea-nav-link">API</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="ea-nav-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="ea-nav-link">Issues</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#why" class="ea-nav-link">Why</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="ea-nav-link">Features</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="ea-nav-link">Pricing</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="ea-nav-link">Blog</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="ea-nav-link">Gallery</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="ea-nav-link">Compare</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="ea-nav-link">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="ea-nav-link">{{if .Config.Navigation.LabelAPI}}{{.Config.Navigation.LabelAPI}}{{else}}API{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="ea-nav-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="ea-nav-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#why" class="ea-nav-link">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Why{{end}}</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="ea-nav-link">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="ea-nav-link">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="ea-nav-link">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="ea-nav-link">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="ea-nav-link">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}
|
||||
<a href="{{.RepoURL}}" class="ea-btn-text">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
@@ -1079,7 +1079,7 @@
|
||||
<section class="ea-section" style="padding-top: 100px;">
|
||||
<div class="ea-section-inner">
|
||||
<div class="ea-section-header ea-reveal">
|
||||
<div class="ea-section-eyebrow">Blog</div>
|
||||
<div class="ea-section-eyebrow">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}All Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1112,7 +1112,7 @@
|
||||
</section>
|
||||
{{else}}
|
||||
<!-- Hero Section -->
|
||||
<section class="ea-hero">
|
||||
<section class="ea-hero" id="hero">
|
||||
<div class="ea-reveal">
|
||||
<div class="ea-hero-eyebrow">{{if .Config.Brand.Name}}{{.Config.Brand.Name}}{{else}}Open Source{{end}}</div>
|
||||
</div>
|
||||
@@ -1125,14 +1125,26 @@
|
||||
{{.Config.Hero.PrimaryCTA.Label}}
|
||||
{{svg "octicon-arrow-right" 14}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="{{.RepoURL}}" class="ea-btn-primary" data-cta="primary">
|
||||
Get Started
|
||||
{{svg "octicon-arrow-right" 14}}
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; margin-top: 16px;" class="ea-reveal ea-reveal-delay-4">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="ea-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="ea-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .Config.Hero.CodeExample}}
|
||||
<div class="ea-code-block ea-reveal ea-reveal-delay-4">
|
||||
<button class="ea-copy-btn" onclick="navigator.clipboard.writeText(document.getElementById('code-example').textContent)">
|
||||
@@ -1146,7 +1158,7 @@
|
||||
{{if or .Config.Stats (gt .NumStars 0)}}
|
||||
<div class="ea-rule"><div class="ea-rule-inner"></div></div>
|
||||
<!-- Stats -->
|
||||
<section class="ea-stats">
|
||||
<section class="ea-stats" id="stats">
|
||||
<div class="ea-stats-inner">
|
||||
{{if .Config.Stats}}
|
||||
{{range .Config.Stats}}
|
||||
@@ -1177,7 +1189,7 @@
|
||||
{{end}}
|
||||
|
||||
{{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}}
|
||||
<section class="ea-downloads">
|
||||
<section class="ea-downloads" id="downloads">
|
||||
<div class="ea-section-label ea-reveal">Downloads</div>
|
||||
<h2 class="ea-section-title ea-reveal">v{{.LatestReleaseTag}}</h2>
|
||||
<p style="color: var(--ea-muted); font-size: 17px; margin-bottom: 28px;" class="ea-reveal">Get the latest release</p>
|
||||
@@ -1240,25 +1252,6 @@
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="margin-bottom: 20px;" class="ea-reveal">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-family: 'IBM Plex Mono', monospace; font-size: 11px; color: var(--ea-light); letter-spacing: 0.08em; text-transform: uppercase;">{{svg "octicon-device-mobile" 14}} App Stores</h4>
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="ea-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="ea-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if $otherFiles}}
|
||||
<div style="margin-bottom: 20px;" class="ea-reveal">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-family: 'IBM Plex Mono', monospace; font-size: 11px; color: var(--ea-light); letter-spacing: 0.08em; text-transform: uppercase;">{{svg "octicon-file" 14}} Other</h4>
|
||||
@@ -1274,8 +1267,8 @@
|
||||
<!-- Value Props -->
|
||||
{{if .Config.ValueProps}}
|
||||
<section class="ea-section" id="why">
|
||||
<div class="ea-section-label ea-reveal">Why choose this</div>
|
||||
<h2 class="ea-section-title ea-reveal">{{if .Config.Brand.Name}}Why {{.Config.Brand.Name}}{{else}}Why Choose Us{{end}}</h2>
|
||||
<div class="ea-section-label ea-reveal">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Why choose this{{end}}</div>
|
||||
<h2 class="ea-section-title ea-reveal">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}{{if .Config.Brand.Name}}Why {{.Config.Brand.Name}}{{else}}Why Choose Us{{end}}{{end}}</h2>
|
||||
{{range $i, $v := .Config.ValueProps}}
|
||||
<div class="ea-value-item ea-reveal">
|
||||
<div class="ea-value-number">{{$i | printf "%d"}}</div>
|
||||
@@ -1291,8 +1284,8 @@
|
||||
<!-- Features Accordion -->
|
||||
{{if .Config.Features}}
|
||||
<section class="ea-section" id="features">
|
||||
<div class="ea-section-label ea-reveal">Capabilities</div>
|
||||
<h2 class="ea-section-title ea-reveal">Features</h2>
|
||||
<div class="ea-section-label ea-reveal">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Capabilities{{end}}</div>
|
||||
<h2 class="ea-section-title ea-reveal">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</h2>
|
||||
<div class="ea-accordion ea-reveal">
|
||||
{{range $i, $f := .Config.Features}}
|
||||
<div class="ea-accordion-item" data-index="{{$i}}">
|
||||
@@ -1312,7 +1305,7 @@
|
||||
<!-- Pricing Section -->
|
||||
{{if .Config.Pricing.Plans}}
|
||||
<section class="ea-pricing" id="pricing">
|
||||
<div class="ea-section-label ea-reveal" style="max-width: 780px; margin: 0 auto 20px;">Investment</div>
|
||||
<div class="ea-section-label ea-reveal" style="max-width: 780px; margin: 0 auto 20px;">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Investment{{end}}</div>
|
||||
<h2 class="ea-pricing-title ea-reveal">{{if .Config.Pricing.Headline}}{{.Config.Pricing.Headline}}{{else}}Pricing{{end}}</h2>
|
||||
<div class="ea-pricing-grid ea-reveal">
|
||||
{{range .Config.Pricing.Plans}}
|
||||
@@ -1337,7 +1330,7 @@
|
||||
|
||||
<!-- Testimonial -->
|
||||
{{if .Config.SocialProof.Testimonials}}
|
||||
<section class="ea-testimonial-section">
|
||||
<section class="ea-testimonial-section" id="testimonials">
|
||||
<div class="ea-testimonial-inner">
|
||||
<div class="ea-testimonials-container">
|
||||
{{range .Config.SocialProof.Testimonials}}
|
||||
@@ -1369,7 +1362,7 @@
|
||||
|
||||
<!-- Used By -->
|
||||
{{if .Config.SocialProof.Logos}}
|
||||
<section class="ea-used-by">
|
||||
<section class="ea-used-by" id="social-proof">
|
||||
<div class="ea-section-label ea-reveal">Trusted by</div>
|
||||
<div class="ea-used-by-logos ea-reveal ea-reveal-delay-1">
|
||||
{{range .Config.SocialProof.Logos}}
|
||||
@@ -1381,7 +1374,7 @@
|
||||
|
||||
<!-- CTA Section -->
|
||||
{{if .Config.CTASection.Headline}}
|
||||
<section class="ea-cta-section">
|
||||
<section class="ea-cta-section" id="cta">
|
||||
<h2 class="ea-reveal">{{.Config.CTASection.Headline}}</h2>
|
||||
{{if .Config.CTASection.Subheadline}}
|
||||
<button class="ea-install-cmd ea-reveal ea-reveal-delay-1" onclick="navigator.clipboard.writeText('{{.Config.CTASection.Subheadline}}')">
|
||||
@@ -1404,7 +1397,7 @@
|
||||
<!-- Blog Section -->
|
||||
{{if and .Config.Blog.Enabled .BlogPosts}}
|
||||
<section class="ea-section" id="blog">
|
||||
<div class="ea-section-label ea-reveal">Blog</div>
|
||||
<div class="ea-section-label ea-reveal">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2 class="ea-section-title ea-reveal">{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}Latest Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p style="color: var(--ea-muted); font-size: 17px; margin-bottom: 32px;" class="ea-reveal">{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
{{range .BlogPosts}}
|
||||
@@ -1439,7 +1432,7 @@
|
||||
<!-- Gallery Section -->
|
||||
{{if and .Config.Gallery.Enabled .GalleryImages}}
|
||||
<section class="ea-section" id="gallery">
|
||||
<div class="ea-section-label ea-reveal">Gallery</div>
|
||||
<div class="ea-section-label ea-reveal">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</div>
|
||||
<h2 class="ea-section-title ea-reveal">{{if .Config.Gallery.Headline}}{{.Config.Gallery.Headline}}{{else}}Gallery{{end}}</h2>
|
||||
{{if .Config.Gallery.Subheadline}}<p style="color: var(--ea-muted); font-size: 17px; margin-bottom: 32px;" class="ea-reveal">{{.Config.Gallery.Subheadline}}</p>{{end}}
|
||||
<div style="display: grid; grid-template-columns: repeat({{if .Config.Gallery.Columns}}{{.Config.Gallery.Columns}}{{else}}3{{end}}, 1fr); gap: 16px;">
|
||||
@@ -1462,7 +1455,7 @@
|
||||
<section class="ea-section" id="comparison" style="border-top: 1px solid var(--ea-border);">
|
||||
<div class="ea-section-inner">
|
||||
<div class="ea-section-header ea-reveal">
|
||||
<div class="ea-section-label">Compare</div>
|
||||
<div class="ea-section-label">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</div>
|
||||
<h2>{{if .Config.Comparison.Headline}}{{.Config.Comparison.Headline}}{{else}}How We Compare{{end}}</h2>
|
||||
{{if .Config.Comparison.Subheadline}}<p>{{.Config.Comparison.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1542,8 +1535,8 @@
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}<a href="{{.RepoURL}}" class="ea-footer-link">Repository</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="ea-footer-link">Documentation</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="ea-footer-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="ea-footer-link">Issues</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="ea-footer-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="ea-footer-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -986,16 +986,16 @@
|
||||
{{range .Config.Footer.Links}}
|
||||
<a href="{{.URL}}" class="osh-nav-link">{{.Label}}</a>
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="osh-nav-link">Docs</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="osh-nav-link">API</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="osh-nav-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="osh-nav-link">Issues</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="osh-nav-link">Why Us</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="osh-nav-link">Features</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="osh-nav-link">Pricing</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="osh-nav-link">Blog</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="osh-nav-link">Gallery</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="osh-nav-link">Compare</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="osh-nav-link">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="osh-nav-link">{{if .Config.Navigation.LabelAPI}}{{.Config.Navigation.LabelAPI}}{{else}}API{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="osh-nav-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="osh-nav-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="osh-nav-link">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Why Us{{end}}</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="osh-nav-link">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="osh-nav-link">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="osh-nav-link">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="osh-nav-link">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="osh-nav-link">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}
|
||||
<a href="{{.RepoURL}}" class="osh-nav-cta">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
@@ -1051,7 +1051,7 @@
|
||||
<section class="osh-features" style="padding-top: 120px;">
|
||||
<div class="osh-features-inner">
|
||||
<div class="osh-section-header osh-reveal">
|
||||
<div class="osh-section-label">Blog</div>
|
||||
<div class="osh-section-label">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}All Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1082,7 +1082,7 @@
|
||||
</section>
|
||||
{{else}}
|
||||
<!-- Hero Section -->
|
||||
<section class="osh-hero">
|
||||
<section class="osh-hero" id="hero">
|
||||
<div class="osh-hero-gradient"></div>
|
||||
|
||||
<div class="osh-hero-content">
|
||||
@@ -1103,11 +1103,6 @@
|
||||
{{.Config.Hero.PrimaryCTA.Label}}
|
||||
{{svg "octicon-arrow-right" 16}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="{{.RepoURL}}" class="osh-btn-primary" data-cta="primary">
|
||||
Get Started
|
||||
{{svg "octicon-arrow-right" 16}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
{{if .Config.Hero.SecondaryCTA.Label}}
|
||||
@@ -1115,14 +1110,26 @@
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
{{.Config.Hero.SecondaryCTA.Label}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="{{.RepoURL}}" class="osh-btn-secondary" data-cta="secondary">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
View Source
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; margin-top: 16px;" class="osh-reveal visible osh-reveal-delay-4">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="osh-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="osh-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .Config.Hero.CodeExample}}
|
||||
<div class="osh-code-block osh-reveal visible osh-reveal-delay-4">
|
||||
<span class="osh-code-prompt">$</span>
|
||||
@@ -1137,7 +1144,7 @@
|
||||
|
||||
<!-- Stats Section -->
|
||||
{{if or .Config.Stats (gt .NumStars 0)}}
|
||||
<section class="osh-stats">
|
||||
<section class="osh-stats" id="stats">
|
||||
<div class="osh-stats-inner osh-reveal">
|
||||
{{if .Config.Stats}}
|
||||
{{range .Config.Stats}}
|
||||
@@ -1168,7 +1175,7 @@
|
||||
|
||||
<!-- Downloads Section -->
|
||||
{{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}}
|
||||
<section class="osh-downloads">
|
||||
<section class="osh-downloads" id="downloads">
|
||||
<div class="osh-downloads-inner osh-reveal">
|
||||
<h2>Download v{{.LatestReleaseTag}}</h2>
|
||||
<p>Get the latest release</p>
|
||||
@@ -1231,25 +1238,6 @@
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 14px; color: var(--osh-muted); font-family: 'IBM Plex Mono', monospace; letter-spacing: 0.05em; text-transform: uppercase;">{{svg "octicon-device-mobile" 16}} App Stores</h4>
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="osh-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="osh-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if $otherFiles}}
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 14px; color: var(--osh-muted); font-family: 'IBM Plex Mono', monospace; letter-spacing: 0.05em; text-transform: uppercase;">{{svg "octicon-file" 16}} Other</h4>
|
||||
@@ -1267,10 +1255,9 @@
|
||||
<section class="osh-features" id="value-props">
|
||||
<div class="osh-features-inner">
|
||||
<div class="osh-section-header osh-reveal">
|
||||
<div class="osh-section-label">Why choose us</div>
|
||||
<h2>{{if .Config.Brand.Name}}Why {{.Config.Brand.Name}}?{{else}}Why Choose Us{{end}}</h2>
|
||||
<p>Everything you need to get started quickly.</p>
|
||||
</div>
|
||||
<div class="osh-section-label">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Why choose us{{end}}</div>
|
||||
<h2>{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}{{if .Config.Brand.Name}}Why {{.Config.Brand.Name}}?{{else}}Why Choose Us{{end}}{{end}}</h2>
|
||||
</div>
|
||||
<div class="osh-features-grid">
|
||||
{{range .Config.ValueProps}}
|
||||
<div class="osh-feature-card osh-reveal">
|
||||
@@ -1291,10 +1278,9 @@
|
||||
<section class="osh-features" id="features" style="padding-top: 40px;">
|
||||
<div class="osh-features-inner">
|
||||
<div class="osh-section-header osh-reveal">
|
||||
<div class="osh-section-label">Capabilities</div>
|
||||
<h2>Features</h2>
|
||||
<p>Powerful capabilities at your fingertips.</p>
|
||||
</div>
|
||||
<div class="osh-section-label">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Capabilities{{end}}</div>
|
||||
<h2>{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</h2>
|
||||
</div>
|
||||
<div class="osh-features-grid">
|
||||
{{range .Config.Features}}
|
||||
<div class="osh-feature-card osh-reveal">
|
||||
@@ -1312,7 +1298,7 @@
|
||||
|
||||
<!-- Social Proof -->
|
||||
{{if or .Config.SocialProof.Logos .Config.SocialProof.Testimonials}}
|
||||
<section class="osh-social-proof">
|
||||
<section class="osh-social-proof" id="social-proof">
|
||||
<div class="osh-social-proof-inner">
|
||||
{{if .Config.SocialProof.Logos}}
|
||||
<div class="osh-logos osh-reveal">
|
||||
@@ -1352,7 +1338,7 @@
|
||||
<section class="osh-pricing" id="pricing">
|
||||
<div class="osh-pricing-inner">
|
||||
<div class="osh-section-header osh-reveal">
|
||||
<div class="osh-section-label">Pricing</div>
|
||||
<div class="osh-section-label">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</div>
|
||||
<h2>{{if .Config.Pricing.Headline}}{{.Config.Pricing.Headline}}{{else}}Pricing{{end}}</h2>
|
||||
<p>{{if .Config.Pricing.Subheadline}}{{.Config.Pricing.Subheadline}}{{else}}Choose the plan that works for you{{end}}</p>
|
||||
</div>
|
||||
@@ -1378,7 +1364,7 @@
|
||||
|
||||
<!-- CTA Section -->
|
||||
{{if .Config.CTASection.Headline}}
|
||||
<section class="osh-cta-section">
|
||||
<section class="osh-cta-section" id="cta">
|
||||
<div class="osh-cta-inner osh-reveal">
|
||||
<h2>{{.Config.CTASection.Headline}}</h2>
|
||||
{{if .Config.CTASection.Subheadline}}
|
||||
@@ -1397,7 +1383,7 @@
|
||||
<section class="osh-features" id="blog" style="border-top: 1px solid rgba(255,255,255,0.04);">
|
||||
<div class="osh-features-inner">
|
||||
<div class="osh-section-header osh-reveal">
|
||||
<div class="osh-section-label">Blog</div>
|
||||
<div class="osh-section-label">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}Latest Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1436,7 +1422,7 @@
|
||||
<section class="osh-features" id="gallery" style="border-top: 1px solid rgba(255,255,255,0.04);">
|
||||
<div class="osh-features-inner">
|
||||
<div class="osh-section-header osh-reveal">
|
||||
<div class="osh-section-label">Gallery</div>
|
||||
<div class="osh-section-label">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</div>
|
||||
<h2>{{if .Config.Gallery.Headline}}{{.Config.Gallery.Headline}}{{else}}Gallery{{end}}</h2>
|
||||
{{if .Config.Gallery.Subheadline}}<p>{{.Config.Gallery.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1461,7 +1447,7 @@
|
||||
<section class="osh-features" id="comparison" style="border-top: 1px solid rgba(255,255,255,0.04);">
|
||||
<div class="osh-features-inner">
|
||||
<div class="osh-section-header osh-reveal">
|
||||
<div class="osh-section-label">Compare</div>
|
||||
<div class="osh-section-label">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</div>
|
||||
<h2>{{if .Config.Comparison.Headline}}{{.Config.Comparison.Headline}}{{else}}How We Compare{{end}}</h2>
|
||||
{{if .Config.Comparison.Subheadline}}<p>{{.Config.Comparison.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1544,8 +1530,8 @@
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}<a href="{{.RepoURL}}" class="osh-footer-link">Repository</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="osh-footer-link">Documentation</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="osh-footer-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="osh-footer-link">Issues</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="osh-footer-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="osh-footer-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
.gm-reveal-delay-1 { transition-delay: 0.12s; }
|
||||
.gm-reveal-delay-2 { transition-delay: 0.24s; }
|
||||
.gm-reveal-delay-3 { transition-delay: 0.36s; }
|
||||
.gm-reveal-delay-4 { transition-delay: 0.48s; }
|
||||
|
||||
.gm-page {
|
||||
min-height: 100vh;
|
||||
@@ -1103,16 +1104,16 @@
|
||||
{{range .Config.Footer.Links}}
|
||||
<a href="{{.URL}}" class="gm-nav-link">{{.Label}}</a>
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="gm-nav-link">Docs</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="gm-nav-link">API</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="gm-nav-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="gm-nav-link">Issues</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="gm-nav-link">Why</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="gm-nav-link">Features</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="gm-nav-link">Pricing</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="gm-nav-link">Blog</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="gm-nav-link">Gallery</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="gm-nav-link">Compare</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="gm-nav-link">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="gm-nav-link">{{if .Config.Navigation.LabelAPI}}{{.Config.Navigation.LabelAPI}}{{else}}API{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="gm-nav-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="gm-nav-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="gm-nav-link">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Why{{end}}</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="gm-nav-link">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="gm-nav-link">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="gm-nav-link">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="gm-nav-link">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="gm-nav-link">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}
|
||||
<a href="{{.RepoURL}}" class="gm-nav-repo">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
@@ -1131,9 +1132,11 @@
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<a href="{{if .Config.Hero.PrimaryCTA.URL}}{{.Config.Hero.PrimaryCTA.URL}}{{else}}{{.RepoURL}}{{end}}" class="gm-btn-primary" data-cta="primary" style="padding: 10px 20px; font-size: 13px;">
|
||||
<span>{{if .Config.Hero.PrimaryCTA.Label}}{{.Config.Hero.PrimaryCTA.Label}}{{else}}Get Started{{end}}</span>
|
||||
{{if .Config.Hero.PrimaryCTA.Label}}
|
||||
<a href="{{.Config.Hero.PrimaryCTA.URL}}" class="gm-btn-primary" data-cta="primary" style="padding: 10px 20px; font-size: 13px;">
|
||||
<span>{{.Config.Hero.PrimaryCTA.Label}}</span>
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<button class="gm-mobile-toggle" onclick="document.getElementById('gm-mobile-nav').classList.toggle('open')">Menu</button>
|
||||
</nav>
|
||||
@@ -1141,16 +1144,16 @@
|
||||
{{range .Config.Footer.Links}}
|
||||
<a href="{{.URL}}" class="gm-nav-link">{{.Label}}</a>
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="gm-nav-link">Docs</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="gm-nav-link">API</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="gm-nav-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="gm-nav-link">Issues</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="gm-nav-link">Why</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="gm-nav-link">Features</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="gm-nav-link">Pricing</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="gm-nav-link">Blog</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="gm-nav-link">Gallery</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="gm-nav-link">Compare</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="gm-nav-link">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="gm-nav-link">{{if .Config.Navigation.LabelAPI}}{{.Config.Navigation.LabelAPI}}{{else}}API{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="gm-nav-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="gm-nav-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="gm-nav-link">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Why{{end}}</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="gm-nav-link">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="gm-nav-link">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="gm-nav-link">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="gm-nav-link">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="gm-nav-link">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}
|
||||
<a href="{{.RepoURL}}" class="gm-nav-repo">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
@@ -1190,7 +1193,7 @@
|
||||
<section class="gm-section" style="padding-top: 120px;">
|
||||
<div class="gm-section-inner">
|
||||
<div class="gm-section-header gm-reveal">
|
||||
<div class="gm-section-eyebrow">Blog</div>
|
||||
<div class="gm-section-eyebrow">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}All Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1221,7 +1224,7 @@
|
||||
</section>
|
||||
{{else}}
|
||||
<!-- Hero Section -->
|
||||
<section class="gm-hero">
|
||||
<section class="gm-hero" id="hero">
|
||||
<div class="gm-hero-inner">
|
||||
<div>
|
||||
{{if gt .NumStars 100}}
|
||||
@@ -1238,12 +1241,14 @@
|
||||
</p>
|
||||
|
||||
<div class="gm-hero-ctas gm-reveal gm-reveal-delay-3">
|
||||
<a href="{{if .Config.Hero.PrimaryCTA.URL}}{{.Config.Hero.PrimaryCTA.URL}}{{else}}{{.RepoURL}}{{end}}" class="gm-btn-primary" data-cta="primary">
|
||||
{{if .Config.Hero.PrimaryCTA.Label}}
|
||||
<a href="{{.Config.Hero.PrimaryCTA.URL}}" class="gm-btn-primary" data-cta="primary">
|
||||
<span>
|
||||
{{if .Config.Hero.PrimaryCTA.Label}}{{.Config.Hero.PrimaryCTA.Label}}{{else}}Get Started{{end}}
|
||||
{{.Config.Hero.PrimaryCTA.Label}}
|
||||
{{svg "octicon-arrow-right" 16}}
|
||||
</span>
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .Config.Hero.SecondaryCTA.Label}}
|
||||
<a href="{{.Config.Hero.SecondaryCTA.URL}}" class="gm-btn-secondary" data-cta="secondary">
|
||||
{{svg "octicon-play" 16}}
|
||||
@@ -1252,6 +1257,23 @@
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; margin-top: 16px;" class="gm-reveal gm-reveal-delay-4">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="gm-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="gm-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="gm-hero-note gm-reveal gm-reveal-delay-3">
|
||||
<span>{{svg "octicon-check" 14}} Free to use</span>
|
||||
<span>{{svg "octicon-check" 14}} Open source</span>
|
||||
@@ -1280,7 +1302,7 @@
|
||||
|
||||
<!-- Stats Section -->
|
||||
{{if .Config.Stats}}
|
||||
<section class="gm-stats">
|
||||
<section class="gm-stats" id="stats">
|
||||
<div class="gm-stats-grid">
|
||||
{{range .Config.Stats}}
|
||||
<div class="gm-stat-card gm-reveal">
|
||||
@@ -1293,7 +1315,7 @@
|
||||
{{end}}
|
||||
|
||||
{{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}}
|
||||
<section class="gm-downloads">
|
||||
<section class="gm-downloads" id="downloads">
|
||||
<div class="gm-downloads-inner">
|
||||
<div class="gm-downloads-header gm-reveal">
|
||||
<h2>Download v{{.LatestReleaseTag}}</h2>
|
||||
@@ -1358,25 +1380,6 @@
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 14px; color: var(--gm-light); font-weight: 600;">{{svg "octicon-device-mobile" 16}} App Stores</h4>
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="gm-download-item gm-reveal" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="gm-download-item gm-reveal" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if $otherFiles}}
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 14px; color: var(--gm-light); font-weight: 600;">{{svg "octicon-file" 16}} Other</h4>
|
||||
@@ -1391,7 +1394,7 @@
|
||||
|
||||
<!-- Trust Bar -->
|
||||
{{if .Config.SocialProof.Logos}}
|
||||
<section class="gm-trust">
|
||||
<section class="gm-trust" id="social-proof">
|
||||
<p class="gm-trust-label gm-reveal">Trusted by teams at</p>
|
||||
<div class="gm-trust-bar gm-reveal gm-reveal-delay-1">
|
||||
{{range .Config.SocialProof.Logos}}
|
||||
@@ -1450,7 +1453,7 @@
|
||||
|
||||
<!-- Testimonials -->
|
||||
{{if .Config.SocialProof.Testimonials}}
|
||||
<section class="gm-section">
|
||||
<section class="gm-section" id="testimonials">
|
||||
<div class="gm-testimonial-card">
|
||||
<div class="gm-testimonials-container">
|
||||
{{range .Config.SocialProof.Testimonials}}
|
||||
@@ -1518,7 +1521,7 @@
|
||||
|
||||
<!-- Final CTA -->
|
||||
{{if .Config.CTASection.Headline}}
|
||||
<section class="gm-section">
|
||||
<section class="gm-section" id="cta">
|
||||
<div class="gm-cta-gradient gm-reveal">
|
||||
<h2>{{.Config.CTASection.Headline}}</h2>
|
||||
{{if .Config.CTASection.Subheadline}}
|
||||
@@ -1601,7 +1604,7 @@
|
||||
<section class="gm-section" id="comparison" style="border-top: 1px solid rgba(255,255,255,0.06);">
|
||||
<div class="gm-section-inner">
|
||||
<div class="gm-section-header gm-reveal">
|
||||
<div class="gm-section-label">Compare</div>
|
||||
<div class="gm-section-label">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</div>
|
||||
<h2>{{if .Config.Comparison.Headline}}{{.Config.Comparison.Headline}}{{else}}How We Compare{{end}}</h2>
|
||||
{{if .Config.Comparison.Subheadline}}<p>{{.Config.Comparison.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1683,9 +1686,9 @@
|
||||
<a href="{{.URL}}" class="gm-footer-link">{{.Label}}</a>
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}<a href="{{.RepoURL}}" class="gm-footer-link">Repository</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="gm-footer-link">Docs</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="gm-footer-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="gm-footer-link">Issues</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="gm-footer-link">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="gm-footer-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="gm-footer-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -1022,16 +1022,16 @@
|
||||
{{range .Config.Footer.Links}}
|
||||
<a href="{{.URL}}" class="vs-nav-link">{{.Label}}</a>
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="vs-nav-link">Docs</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="vs-nav-link">API</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="vs-nav-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="vs-nav-link">Issues</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="vs-nav-link">Value Props</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="vs-nav-link">Features</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="vs-nav-link">Pricing</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="vs-nav-link">Blog</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="vs-nav-link">Gallery</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="vs-nav-link">Compare</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="vs-nav-link">{{if .Config.Navigation.LabelDocs}}{{.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="vs-nav-link">{{if .Config.Navigation.LabelAPI}}{{.Config.Navigation.LabelAPI}}{{else}}API{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="vs-nav-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="vs-nav-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="vs-nav-link">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Value Props{{end}}</a>{{end}}
|
||||
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="vs-nav-link">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</a>{{end}}
|
||||
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="vs-nav-link">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</a>{{end}}
|
||||
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="vs-nav-link">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</a>{{end}}
|
||||
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="vs-nav-link">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</a>{{end}}
|
||||
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="vs-nav-link">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}
|
||||
<a href="{{.RepoURL}}" class="vs-nav-cta">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
@@ -1087,7 +1087,7 @@
|
||||
<section class="vs-features" style="padding-top: 140px;">
|
||||
<div class="vs-features-inner">
|
||||
<div class="vs-section-header vs-reveal">
|
||||
<div class="vs-section-label">Blog</div>
|
||||
<div class="vs-section-label">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}All Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1118,7 +1118,7 @@
|
||||
</section>
|
||||
{{else}}
|
||||
<!-- Hero Section -->
|
||||
<section class="vs-hero">
|
||||
<section class="vs-hero" id="hero">
|
||||
<div class="vs-hero-mesh">
|
||||
<div class="vs-hero-blob-1"></div>
|
||||
<div class="vs-hero-blob-2"></div>
|
||||
@@ -1143,11 +1143,6 @@
|
||||
{{.Config.Hero.PrimaryCTA.Label}}
|
||||
{{svg "octicon-arrow-right" 16}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="{{.RepoURL}}" class="vs-btn-primary" data-cta="primary">
|
||||
Get Started
|
||||
{{svg "octicon-arrow-right" 16}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
{{if .Config.Hero.SecondaryCTA.Label}}
|
||||
@@ -1155,14 +1150,26 @@
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
{{.Config.Hero.SecondaryCTA.Label}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="{{.RepoURL}}" class="vs-btn-secondary" data-cta="secondary">
|
||||
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
|
||||
View Source
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; margin-top: 16px;" class="vs-reveal visible vs-reveal-delay-4">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="vs-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="vs-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .Config.Hero.CodeExample}}
|
||||
<div class="vs-code-block vs-reveal visible vs-reveal-delay-4">
|
||||
<span class="vs-code-prompt">$</span>
|
||||
@@ -1177,7 +1184,7 @@
|
||||
|
||||
<!-- Stats Section -->
|
||||
{{if or .Config.Stats (gt .NumStars 0)}}
|
||||
<section class="vs-stats">
|
||||
<section class="vs-stats" id="stats">
|
||||
<div class="vs-stats-inner vs-reveal">
|
||||
{{if .Config.Stats}}
|
||||
{{range .Config.Stats}}
|
||||
@@ -1208,7 +1215,7 @@
|
||||
|
||||
<!-- Downloads Section -->
|
||||
{{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}}
|
||||
<section class="vs-downloads">
|
||||
<section class="vs-downloads" id="downloads">
|
||||
<div class="vs-downloads-inner vs-reveal">
|
||||
<h2>Download v{{.LatestReleaseTag}}</h2>
|
||||
<p>Get the latest release</p>
|
||||
@@ -1271,25 +1278,6 @@
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if or $.GooglePlayID $.AppStoreID}}
|
||||
<div style="margin-bottom: 28px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 14px; font-size: 13px; color: var(--vs-muted); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600;">{{svg "octicon-device-mobile" 16}} App Stores</h4>
|
||||
<div style="display: flex; gap: 14px; flex-wrap: wrap; justify-content: center;">
|
||||
{{if $.GooglePlayID}}
|
||||
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="vs-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
|
||||
Google Play
|
||||
</a>
|
||||
{{end}}
|
||||
{{if $.AppStoreID}}
|
||||
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="vs-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
App Store
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if $otherFiles}}
|
||||
<div style="margin-bottom: 28px;">
|
||||
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 14px; font-size: 13px; color: var(--vs-muted); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600;">{{svg "octicon-file" 16}} Other</h4>
|
||||
@@ -1307,10 +1295,9 @@
|
||||
<section class="vs-features" id="value-props" style="background: var(--vs-surface);">
|
||||
<div class="vs-features-inner">
|
||||
<div class="vs-section-header vs-reveal">
|
||||
<div class="vs-section-label">Why choose us</div>
|
||||
<h2>{{if .Config.Brand.Name}}Why {{.Config.Brand.Name}}?{{else}}Why Choose Us{{end}}</h2>
|
||||
<p>Everything you need to get started quickly.</p>
|
||||
</div>
|
||||
<div class="vs-section-label">{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}Why choose us{{end}}</div>
|
||||
<h2>{{if .Config.Navigation.LabelValueProps}}{{.Config.Navigation.LabelValueProps}}{{else}}{{if .Config.Brand.Name}}Why {{.Config.Brand.Name}}?{{else}}Why Choose Us{{end}}{{end}}</h2>
|
||||
</div>
|
||||
<div class="vs-features-grid">
|
||||
{{range .Config.ValueProps}}
|
||||
<div class="vs-feature-card vs-reveal">
|
||||
@@ -1331,10 +1318,9 @@
|
||||
<section class="vs-features" id="features" style="padding-top: 40px;">
|
||||
<div class="vs-features-inner">
|
||||
<div class="vs-section-header vs-reveal">
|
||||
<div class="vs-section-label">Capabilities</div>
|
||||
<h2>Features</h2>
|
||||
<p>Powerful capabilities at your fingertips.</p>
|
||||
</div>
|
||||
<div class="vs-section-label">{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Capabilities{{end}}</div>
|
||||
<h2>{{if .Config.Navigation.LabelFeatures}}{{.Config.Navigation.LabelFeatures}}{{else}}Features{{end}}</h2>
|
||||
</div>
|
||||
<div class="vs-features-grid">
|
||||
{{range .Config.Features}}
|
||||
<div class="vs-feature-card vs-reveal">
|
||||
@@ -1352,7 +1338,7 @@
|
||||
|
||||
<!-- Social Proof -->
|
||||
{{if or .Config.SocialProof.Logos .Config.SocialProof.Testimonials}}
|
||||
<section class="vs-social-proof">
|
||||
<section class="vs-social-proof" id="social-proof">
|
||||
<div class="vs-social-proof-inner">
|
||||
{{if .Config.SocialProof.Logos}}
|
||||
<div class="vs-logos vs-reveal">
|
||||
@@ -1392,7 +1378,7 @@
|
||||
<section class="vs-pricing" id="pricing">
|
||||
<div class="vs-pricing-inner">
|
||||
<div class="vs-section-header vs-reveal">
|
||||
<div class="vs-section-label">Pricing</div>
|
||||
<div class="vs-section-label">{{if .Config.Navigation.LabelPricing}}{{.Config.Navigation.LabelPricing}}{{else}}Pricing{{end}}</div>
|
||||
<h2>{{if .Config.Pricing.Headline}}{{.Config.Pricing.Headline}}{{else}}Pricing{{end}}</h2>
|
||||
<p>{{if .Config.Pricing.Subheadline}}{{.Config.Pricing.Subheadline}}{{else}}Choose the plan that works for you{{end}}</p>
|
||||
</div>
|
||||
@@ -1418,7 +1404,7 @@
|
||||
|
||||
<!-- CTA Section -->
|
||||
{{if .Config.CTASection.Headline}}
|
||||
<section class="vs-cta-section">
|
||||
<section class="vs-cta-section" id="cta">
|
||||
<div class="vs-cta-inner vs-reveal">
|
||||
<h2>{{.Config.CTASection.Headline}}</h2>
|
||||
{{if .Config.CTASection.Subheadline}}
|
||||
@@ -1437,7 +1423,7 @@
|
||||
<section class="vs-features" id="blog" style="border-top: 1px solid var(--vs-border); background: var(--vs-surface);">
|
||||
<div class="vs-features-inner">
|
||||
<div class="vs-section-header vs-reveal">
|
||||
<div class="vs-section-label">Blog</div>
|
||||
<div class="vs-section-label">{{if .Config.Navigation.LabelBlog}}{{.Config.Navigation.LabelBlog}}{{else}}Blog{{end}}</div>
|
||||
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}Latest Posts{{end}}</h2>
|
||||
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1476,7 +1462,7 @@
|
||||
<section class="vs-features" id="gallery" style="border-top: 1px solid var(--vs-border);">
|
||||
<div class="vs-features-inner">
|
||||
<div class="vs-section-header vs-reveal">
|
||||
<div class="vs-section-label">Gallery</div>
|
||||
<div class="vs-section-label">{{if .Config.Navigation.LabelGallery}}{{.Config.Navigation.LabelGallery}}{{else}}Gallery{{end}}</div>
|
||||
<h2>{{if .Config.Gallery.Headline}}{{.Config.Gallery.Headline}}{{else}}Gallery{{end}}</h2>
|
||||
{{if .Config.Gallery.Subheadline}}<p>{{.Config.Gallery.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1501,7 +1487,7 @@
|
||||
<section class="vs-features" id="comparison" style="border-top: 1px solid var(--vs-border);">
|
||||
<div class="vs-features-inner">
|
||||
<div class="vs-section-header vs-reveal">
|
||||
<div class="vs-section-label">Compare</div>
|
||||
<div class="vs-section-label">{{if .Config.Navigation.LabelCompare}}{{.Config.Navigation.LabelCompare}}{{else}}Compare{{end}}</div>
|
||||
<h2>{{if .Config.Comparison.Headline}}{{.Config.Comparison.Headline}}{{else}}How We Compare{{end}}</h2>
|
||||
{{if .Config.Comparison.Subheadline}}<p>{{.Config.Comparison.Subheadline}}</p>{{end}}
|
||||
</div>
|
||||
@@ -1584,8 +1570,8 @@
|
||||
{{end}}
|
||||
{{if .Config.Navigation.ShowRepository}}<a href="{{.RepoURL}}" class="vs-footer-link">Repository</a>{{end}}
|
||||
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="vs-footer-link">Documentation</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="vs-footer-link">Releases</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="vs-footer-link">Issues</a>{{end}}
|
||||
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="vs-footer-link">{{if .Config.Navigation.LabelReleases}}{{.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</a>{{end}}
|
||||
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="vs-footer-link">{{if .Config.Navigation.LabelIssues}}{{.Config.Navigation.LabelIssues}}{{else}}Issues{{end}}</a>{{end}}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<details class="item toggleable-item" {{if or .PageIsSettingsPagesGeneral .PageIsSettingsPagesBrand .PageIsSettingsPagesHero .PageIsSettingsPagesContent .PageIsSettingsPagesSocial .PageIsSettingsPagesPricing .PageIsSettingsPagesFooter .PageIsSettingsPagesTheme}}open{{end}}>
|
||||
<details class="item toggleable-item" {{if or .PageIsSettingsPagesGeneral .PageIsSettingsPagesBrand .PageIsSettingsPagesHero .PageIsSettingsPagesContent .PageIsSettingsPagesComparison .PageIsSettingsPagesSocial .PageIsSettingsPagesPricing .PageIsSettingsPagesFooter .PageIsSettingsPagesTheme .PageIsSettingsPagesLanguages .PageIsSettingsPagesAdvanced}}open{{end}}>
|
||||
<summary>{{ctx.Locale.Tr "repo.settings.pages"}}</summary>
|
||||
<div class="menu">
|
||||
<a class="{{if .PageIsSettingsPagesGeneral}}active {{end}}item" href="{{.RepoLink}}/settings/pages">
|
||||
@@ -68,6 +68,9 @@
|
||||
<a class="{{if .PageIsSettingsPagesContent}}active {{end}}item" href="{{.RepoLink}}/settings/pages/content">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.content"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsPagesComparison}}active {{end}}item" href="{{.RepoLink}}/settings/pages/comparison">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.comparison"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsPagesSocial}}active {{end}}item" href="{{.RepoLink}}/settings/pages/social">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.social"}}
|
||||
</a>
|
||||
@@ -80,6 +83,12 @@
|
||||
<a class="{{if .PageIsSettingsPagesTheme}}active {{end}}item" href="{{.RepoLink}}/settings/pages/theme">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.theme"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsPagesLanguages}}active {{end}}item" href="{{.RepoLink}}/settings/pages/languages">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.languages"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsPagesAdvanced}}active {{end}}item" href="{{.RepoLink}}/settings/pages/advanced">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.advanced"}}
|
||||
</a>
|
||||
</div>
|
||||
</details>
|
||||
<details class="item toggleable-item" {{if or .PageIsSharedSettingsRunners .PageIsSharedSettingsSecrets .PageIsSharedSettingsVariables .PageIsActionsSettingsGeneral}}open{{end}}>
|
||||
|
||||
@@ -29,15 +29,19 @@
|
||||
|
||||
{{if .AIEnabled}}
|
||||
<div class="divider"></div>
|
||||
<form class="ui form" method="post">
|
||||
<form class="ui form" method="post" id="ai-generate-form">
|
||||
{{.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="ai_generate">
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.ai_generate"}}</label>
|
||||
<p class="help">{{ctx.Locale.Tr "repo.settings.pages.ai_generate_desc"}}</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<button class="ui purple button">{{ctx.Locale.Tr "repo.settings.pages.ai_generate_button"}}</button>
|
||||
<div class="field" id="ai-generate-btn-field">
|
||||
<button class="ui purple button" type="submit">{{svg "octicon-copilot" 16}} {{ctx.Locale.Tr "repo.settings.pages.ai_generate_button"}}</button>
|
||||
</div>
|
||||
<div id="ai-generate-loading" class="tw-hidden tw-py-4">
|
||||
<div class="ui active centered inline loader"></div>
|
||||
<p class="tw-text-center tw-mt-2" style="color:var(--color-text-light);">{{ctx.Locale.Tr "repo.settings.pages.ai_generating"}}</p>
|
||||
</div>
|
||||
</form>
|
||||
{{end}}
|
||||
@@ -169,4 +173,17 @@
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
var form = document.getElementById('ai-generate-form');
|
||||
if (!form) return;
|
||||
form.addEventListener('submit', function() {
|
||||
var btn = form.querySelector('button[type="submit"]');
|
||||
btn.classList.add('loading', 'disabled');
|
||||
btn.disabled = true;
|
||||
document.getElementById('ai-generate-btn-field').classList.add('tw-hidden');
|
||||
document.getElementById('ai-generate-loading').classList.remove('tw-hidden');
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{{template "repo/settings/layout_footer" .}}
|
||||
|
||||
82
templates/repo/settings/pages_advanced.tmpl
Normal file
82
templates/repo/settings/pages_advanced.tmpl
Normal file
@@ -0,0 +1,82 @@
|
||||
{{template "repo/settings/layout_head" (dict "ctxData" . "pageClass" "repository settings pages")}}
|
||||
<div class="user-main-content twelve wide column">
|
||||
<h4 class="ui top attached header">{{ctx.Locale.Tr "repo.settings.pages.advanced"}}</h4>
|
||||
<div class="ui attached segment">
|
||||
<form class="ui form" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.static_routes"}}</h5>
|
||||
<p class="help">{{ctx.Locale.Tr "repo.settings.pages.static_routes_desc"}}</p>
|
||||
<div id="routes-container">
|
||||
{{range $i, $route := .Config.Advanced.StaticRoutes}}
|
||||
<div class="field route-item tw-flex tw-items-center tw-gap-2">
|
||||
<input name="static_route_{{$i}}" value="{{$route}}" placeholder="/schema/*" class="tw-flex-1">
|
||||
<button type="button" class="ui red mini icon button" onclick="this.closest('.route-item').remove()">{{svg "octicon-trash" 14}}</button>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if not .Config.Advanced.StaticRoutes}}
|
||||
<div class="field route-item tw-flex tw-items-center tw-gap-2">
|
||||
<input name="static_route_0" placeholder="/schema/*" class="tw-flex-1">
|
||||
<button type="button" class="ui red mini icon button" onclick="this.closest('.route-item').remove()">{{svg "octicon-trash" 14}}</button>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<button type="button" class="ui mini button tw-mb-4" onclick="addRoute()">+ {{ctx.Locale.Tr "repo.settings.pages.add_route"}}</button>
|
||||
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.redirects"}}</h5>
|
||||
<p class="help">{{ctx.Locale.Tr "repo.settings.pages.redirects_desc"}}</p>
|
||||
<div id="redirects-container">
|
||||
{{range $path, $target := .Config.Advanced.Redirects}}
|
||||
<div class="two fields redirect-item">
|
||||
<div class="field"><input name="redirect_from_{{$path}}" value="{{$path}}" placeholder="/old-path"></div>
|
||||
<div class="field tw-flex tw-items-center tw-gap-2">
|
||||
<input name="redirect_to_{{$path}}" value="{{$target}}" placeholder="https://new-url" class="tw-flex-1">
|
||||
<button type="button" class="ui red mini icon button" onclick="this.closest('.redirect-item').remove()">{{svg "octicon-trash" 14}}</button>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if not .Config.Advanced.Redirects}}
|
||||
<div class="two fields redirect-item">
|
||||
<div class="field"><input name="redirect_from_0" placeholder="/old-path"></div>
|
||||
<div class="field tw-flex tw-items-center tw-gap-2">
|
||||
<input name="redirect_to_0" placeholder="https://new-url" class="tw-flex-1">
|
||||
<button type="button" class="ui red mini icon button" onclick="this.closest('.redirect-item').remove()">{{svg "octicon-trash" 14}}</button>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<button type="button" class="ui mini button tw-mb-4" onclick="addRedirect()">+ {{ctx.Locale.Tr "repo.settings.pages.add_redirect"}}</button>
|
||||
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.custom_code"}}</h5>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.custom_css"}}</label>
|
||||
<textarea name="custom_css" rows="4" placeholder="body { }">{{.Config.Advanced.CustomCSS}}</textarea>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.custom_head"}}</label>
|
||||
<textarea name="custom_head" rows="4" placeholder="<meta ...>">{{.Config.Advanced.CustomHead}}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<button class="ui primary button">{{ctx.Locale.Tr "save"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
let routeCount = {{len .Config.Advanced.StaticRoutes}};
|
||||
let redirectCount = {{len .Config.Advanced.Redirects}};
|
||||
if (routeCount === 0) routeCount = 1;
|
||||
if (redirectCount === 0) redirectCount = 1;
|
||||
function addRoute() {
|
||||
const c = document.getElementById('routes-container');
|
||||
c.insertAdjacentHTML('beforeend', `<div class="field route-item tw-flex tw-items-center tw-gap-2"><input name="static_route_${routeCount}" placeholder="/schema/*" class="tw-flex-1"><button type="button" class="ui red mini icon button" onclick="this.closest('.route-item').remove()">{{svg "octicon-trash" 14}}</button></div>`);
|
||||
routeCount++;
|
||||
}
|
||||
function addRedirect() {
|
||||
const c = document.getElementById('redirects-container');
|
||||
c.insertAdjacentHTML('beforeend', `<div class="two fields redirect-item"><div class="field"><input name="redirect_from_${redirectCount}" placeholder="/old-path"></div><div class="field tw-flex tw-items-center tw-gap-2"><input name="redirect_to_${redirectCount}" placeholder="https://new-url" class="tw-flex-1"><button type="button" class="ui red mini icon button" onclick="this.closest('.redirect-item').remove()">{{svg "octicon-trash" 14}}</button></div></div>`);
|
||||
redirectCount++;
|
||||
}
|
||||
</script>
|
||||
{{template "repo/settings/layout_footer" .}}
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.brand_upload_logo"}}</label>
|
||||
<div class="tw-flex tw-gap-2 tw-items-center">
|
||||
<input type="file" name="brand_logo" accept="image/jpeg,image/png,image/webp,image/gif" form="upload-logo-form">
|
||||
<input type="file" name="brand_logo" accept="image/jpeg,image/png,image/webp,image/gif" form="upload-logo-form" data-ays-ignore="true">
|
||||
<button class="ui primary button" type="submit" form="upload-logo-form">
|
||||
{{svg "octicon-upload" 16}} {{ctx.Locale.Tr "repo.settings.pages.brand_upload_btn"}}
|
||||
</button>
|
||||
@@ -35,7 +35,6 @@
|
||||
</div>
|
||||
<div class="ui horizontal divider">{{ctx.Locale.Tr "repo.settings.pages.brand_or"}}</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.brand_logo_url"}}</label>
|
||||
<input name="brand_logo_url" value="{{.Config.Brand.LogoURL}}" placeholder="https://example.com/logo.svg">
|
||||
<p class="help">{{ctx.Locale.Tr "repo.settings.pages.brand_logo_url_help"}}</p>
|
||||
</div>
|
||||
@@ -60,7 +59,7 @@
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.brand_upload_favicon"}}</label>
|
||||
<div class="tw-flex tw-gap-2 tw-items-center">
|
||||
<input type="file" name="brand_favicon" accept="image/jpeg,image/png,image/webp,image/gif,image/x-icon,image/svg+xml" form="upload-favicon-form">
|
||||
<input type="file" name="brand_favicon" accept="image/jpeg,image/png,image/webp,image/gif,image/x-icon,image/svg+xml" form="upload-favicon-form" data-ays-ignore="true">
|
||||
<button class="ui primary button" type="submit" form="upload-favicon-form">
|
||||
{{svg "octicon-upload" 16}} {{ctx.Locale.Tr "repo.settings.pages.brand_upload_btn"}}
|
||||
</button>
|
||||
@@ -69,7 +68,6 @@
|
||||
</div>
|
||||
<div class="ui horizontal divider">{{ctx.Locale.Tr "repo.settings.pages.brand_or"}}</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.brand_favicon_url"}}</label>
|
||||
<input name="brand_favicon_url" value="{{.Config.Brand.FaviconURL}}" placeholder="https://example.com/favicon.ico">
|
||||
<p class="help">{{ctx.Locale.Tr "repo.settings.pages.brand_favicon_url_help"}}</p>
|
||||
</div>
|
||||
|
||||
@@ -123,7 +123,6 @@
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.comparison_enabled_desc"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help">{{ctx.Locale.Tr "repo.settings.pages.comparison_help_link"}}</p>
|
||||
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.stats"}}</h5>
|
||||
<div id="stats-container">
|
||||
|
||||
@@ -35,9 +35,9 @@
|
||||
</div>
|
||||
<div class="ui horizontal divider">{{ctx.Locale.Tr "repo.settings.pages.hero_or"}}</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.image_url"}}</label>
|
||||
<input name="image_url" value="{{.Config.Hero.ImageURL}}" placeholder="https://example.com/hero.png">
|
||||
</div>
|
||||
<br>
|
||||
{{end}}
|
||||
|
||||
<div class="field">
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
{{range $code, $name := .LanguageNames}}
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="languages" value="{{$code}}" {{if $.IsLangEnabled $code}}checked{{end}}>
|
||||
<input type="checkbox" name="languages" value="{{$code}}" {{if index $.EnabledLangs $code}}checked{{end}}>
|
||||
<label>{{$name}} ({{$code}})</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -44,31 +44,44 @@
|
||||
{{ctx.Locale.Tr "repo.settings.pages.translations"}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
{{/* ── Action bar: language selector + buttons ── */}}
|
||||
<div class="tw-flex tw-flex-wrap tw-gap-2 tw-items-center tw-mb-4">
|
||||
<select id="lang-selector" class="ui dropdown" style="min-width:200px;">
|
||||
{{range .Config.I18n.Languages}}
|
||||
{{if ne . $.Config.I18n.DefaultLang}}
|
||||
<option value="{{.}}">{{index $.LanguageNames .}} ({{.}}) {{if index $.TranslationMap .}}✓{{end}}</option>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</select>
|
||||
{{if .AIEnabled}}
|
||||
<form method="post" class="tw-inline-block" id="ai-translate-form">
|
||||
{{.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="ai_translate">
|
||||
<input type="hidden" name="target_lang" id="ai-translate-lang" value="">
|
||||
<button class="ui purple tiny button" type="submit">{{svg "octicon-copilot" 14}} {{ctx.Locale.Tr "repo.settings.pages.ai_translate"}}</button>
|
||||
</form>
|
||||
<form method="post" class="tw-inline-block" id="ai-translate-all-form">
|
||||
{{.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="ai_translate_all">
|
||||
<button class="ui purple tiny button" type="submit">{{svg "octicon-copilot" 14}} {{ctx.Locale.Tr "repo.settings.pages.ai_translate_all"}}</button>
|
||||
</form>
|
||||
{{end}}
|
||||
<form method="post" class="tw-inline-block" id="delete-translation-form" style="display:none;">
|
||||
{{.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="delete_translation">
|
||||
<input type="hidden" name="target_lang" id="delete-translate-lang" value="">
|
||||
<button class="ui red tiny button" type="submit">{{svg "octicon-trash" 14}} {{ctx.Locale.Tr "repo.settings.pages.delete_translation"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="ai-translate-loading" class="tw-hidden tw-py-4">
|
||||
<div class="ui active centered inline loader"></div>
|
||||
<p class="tw-text-center tw-mt-2" style="color:var(--color-text-light);">{{ctx.Locale.Tr "repo.settings.pages.ai_translating"}}</p>
|
||||
</div>
|
||||
|
||||
{{/* ── Language panels (one per non-default language, only one visible at a time) ── */}}
|
||||
{{range .Config.I18n.Languages}}
|
||||
{{if ne . $.Config.I18n.DefaultLang}}
|
||||
<div class="ui segment">
|
||||
<div class="tw-flex tw-justify-between tw-items-center tw-mb-4">
|
||||
<h5 class="tw-m-0">{{index $.LanguageNames .}} ({{.}})</h5>
|
||||
<div>
|
||||
{{if $.AIEnabled}}
|
||||
<form method="post" class="tw-inline-block">
|
||||
{{$.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="ai_translate">
|
||||
<input type="hidden" name="target_lang" value="{{.}}">
|
||||
<button class="ui purple tiny button">{{ctx.Locale.Tr "repo.settings.pages.ai_translate"}}</button>
|
||||
</form>
|
||||
{{end}}
|
||||
{{if index $.TranslationMap .}}
|
||||
<form method="post" class="tw-inline-block">
|
||||
{{$.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="delete_translation">
|
||||
<input type="hidden" name="target_lang" value="{{.}}">
|
||||
<button class="ui red tiny button">{{ctx.Locale.Tr "repo.settings.pages.delete_translation"}}</button>
|
||||
</form>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lang-panel" id="lang-panel-{{.}}" style="display:none;">
|
||||
<form class="ui form" method="post">
|
||||
{{$.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="save_translation">
|
||||
@@ -76,6 +89,25 @@
|
||||
|
||||
{{$trans := index $.TranslationMap .}}
|
||||
|
||||
{{/* ── Brand ── */}}
|
||||
{{if or $.Config.Brand.Name $.Config.Brand.Tagline}}
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.trans_section_brand"}}</h5>
|
||||
{{if $.Config.Brand.Name}}
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_brand_name"}}</label>
|
||||
<input name="trans_brand_name" value="{{if $trans}}{{$trans.BrandName}}{{end}}" placeholder="{{$.Config.Brand.Name}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{if $.Config.Brand.Tagline}}
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_brand_tagline"}}</label>
|
||||
<input name="trans_brand_tagline" value="{{if $trans}}{{$trans.BrandTagline}}{{end}}" placeholder="{{$.Config.Brand.Tagline}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{/* ── Hero ── */}}
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.trans_section_hero"}}</h5>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_headline"}}</label>
|
||||
<input name="trans_headline" value="{{if $trans}}{{$trans.Headline}}{{end}}" placeholder="{{$.Config.Hero.Headline}}">
|
||||
@@ -84,14 +116,110 @@
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_subheadline"}}</label>
|
||||
<input name="trans_subheadline" value="{{if $trans}}{{$trans.Subheadline}}{{end}}" placeholder="{{$.Config.Hero.Subheadline}}">
|
||||
</div>
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_primary_cta"}}</label>
|
||||
<input name="trans_primary_cta" value="{{if $trans}}{{$trans.PrimaryCTA}}{{end}}" placeholder="{{$.Config.Hero.PrimaryCTA.Label}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_secondary_cta"}}</label>
|
||||
<input name="trans_secondary_cta" value="{{if $trans}}{{$trans.SecondaryCTA}}{{end}}" placeholder="{{$.Config.Hero.SecondaryCTA.Label}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{/* ── Stats ── */}}
|
||||
{{if $.Config.Stats}}
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.trans_section_stats"}}</h5>
|
||||
{{range $i, $s := $.Config.Stats}}
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_stat_value"}} — <em>{{$s.Value}}</em></label>
|
||||
<input name="trans_stat_{{$i}}_value" value="{{if $trans}}{{index $trans.StatsValues $i}}{{end}}" placeholder="{{$s.Value}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_stat_label"}} — <em>{{$s.Label}}</em></label>
|
||||
<input name="trans_stat_{{$i}}_label" value="{{if $trans}}{{index $trans.StatsLabels $i}}{{end}}" placeholder="{{$s.Label}}">
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{/* ── Value Props ── */}}
|
||||
{{if $.Config.ValueProps}}
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.trans_section_value_props"}}</h5>
|
||||
{{range $i, $vp := $.Config.ValueProps}}
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_primary_cta"}}</label>
|
||||
<input name="trans_primary_cta" value="{{if $trans}}{{$trans.PrimaryCTA}}{{end}}" placeholder="{{$.Config.Hero.PrimaryCTA.Label}}">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_title"}} — <em>{{$vp.Title}}</em></label>
|
||||
<input name="trans_vp_{{$i}}_title" value="{{if $trans}}{{index $trans.ValuePropTitles $i}}{{end}}" placeholder="{{$vp.Title}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_secondary_cta"}}</label>
|
||||
<input name="trans_secondary_cta" value="{{if $trans}}{{$trans.SecondaryCTA}}{{end}}" placeholder="{{$.Config.Hero.SecondaryCTA.Label}}">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_description"}}</label>
|
||||
<input name="trans_vp_{{$i}}_desc" value="{{if $trans}}{{index $trans.ValuePropDescs $i}}{{end}}" placeholder="{{$vp.Description}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{/* ── Features ── */}}
|
||||
{{if $.Config.Features}}
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.trans_section_features"}}</h5>
|
||||
{{range $i, $f := $.Config.Features}}
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_title"}} — <em>{{$f.Title}}</em></label>
|
||||
<input name="trans_feat_{{$i}}_title" value="{{if $trans}}{{index $trans.FeatureTitles $i}}{{end}}" placeholder="{{$f.Title}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_description"}}</label>
|
||||
<input name="trans_feat_{{$i}}_desc" value="{{if $trans}}{{index $trans.FeatureDescs $i}}{{end}}" placeholder="{{$f.Description}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{/* ── Testimonials ── */}}
|
||||
{{if $.Config.SocialProof.Testimonials}}
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.trans_section_testimonials"}}</h5>
|
||||
{{range $i, $t := $.Config.SocialProof.Testimonials}}
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_quote"}} — <em>{{$t.Author}}</em></label>
|
||||
<textarea name="trans_test_{{$i}}_quote" rows="2" placeholder="{{$t.Quote}}">{{if $trans}}{{index $trans.TestimonialQuotes $i}}{{end}}</textarea>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_role"}}</label>
|
||||
<input name="trans_test_{{$i}}_role" value="{{if $trans}}{{index $trans.TestimonialRoles $i}}{{end}}" placeholder="{{$t.Role}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{/* ── Pricing ── */}}
|
||||
{{if or $.Config.Pricing.Headline $.Config.Pricing.Plans}}
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.trans_section_pricing"}}</h5>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_pricing_headline"}}</label>
|
||||
<input name="trans_pricing_headline" value="{{if $trans}}{{$trans.PricingHeadline}}{{end}}" placeholder="{{$.Config.Pricing.Headline}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_pricing_subheadline"}}</label>
|
||||
<input name="trans_pricing_subheadline" value="{{if $trans}}{{$trans.PricingSubheadline}}{{end}}" placeholder="{{$.Config.Pricing.Subheadline}}">
|
||||
</div>
|
||||
{{range $i, $p := $.Config.Pricing.Plans}}
|
||||
<div class="three fields">
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_plan_name"}} — <em>{{$p.Name}}</em></label>
|
||||
<input name="trans_plan_{{$i}}_name" value="{{if $trans}}{{index $trans.PlanNames $i}}{{end}}" placeholder="{{$p.Name}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_plan_period"}}</label>
|
||||
<input name="trans_plan_{{$i}}_period" value="{{if $trans}}{{index $trans.PlanPeriods $i}}{{end}}" placeholder="{{$p.Period}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_plan_cta"}}</label>
|
||||
<input name="trans_plan_{{$i}}_cta" value="{{if $trans}}{{index $trans.PlanCTAs $i}}{{end}}" placeholder="{{$p.CTA}}">
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{/* ── CTA Section ── */}}
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.trans_section_cta"}}</h5>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_cta_headline"}}</label>
|
||||
<input name="trans_cta_headline" value="{{if $trans}}{{$trans.CTAHeadline}}{{end}}" placeholder="{{$.Config.CTASection.Headline}}">
|
||||
@@ -105,6 +233,128 @@
|
||||
<input name="trans_cta_button" value="{{if $trans}}{{$trans.CTAButton}}{{end}}" placeholder="{{$.Config.CTASection.Button.Label}}">
|
||||
</div>
|
||||
|
||||
{{/* ── Blog ── */}}
|
||||
{{if $.Config.Blog.Enabled}}
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.trans_section_blog"}}</h5>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_blog_headline"}}</label>
|
||||
<input name="trans_blog_headline" value="{{if $trans}}{{$trans.BlogHeadline}}{{end}}" placeholder="{{if $.Config.Blog.Headline}}{{$.Config.Blog.Headline}}{{else}}Latest Posts{{end}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_blog_subheadline"}}</label>
|
||||
<input name="trans_blog_subheadline" value="{{if $trans}}{{$trans.BlogSubheadline}}{{end}}" placeholder="{{$.Config.Blog.Subheadline}}">
|
||||
</div>
|
||||
{{if $.Config.Blog.CTAButton.Label}}
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_blog_cta"}}</label>
|
||||
<input name="trans_blog_cta" value="{{if $trans}}{{$trans.BlogCTAButton}}{{end}}" placeholder="{{$.Config.Blog.CTAButton.Label}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{/* ── Gallery ── */}}
|
||||
{{if $.Config.Gallery.Enabled}}
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.trans_section_gallery"}}</h5>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_gallery_headline"}}</label>
|
||||
<input name="trans_gallery_headline" value="{{if $trans}}{{$trans.GalleryHeadline}}{{end}}" placeholder="{{if $.Config.Gallery.Headline}}{{$.Config.Gallery.Headline}}{{else}}Gallery{{end}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_gallery_subheadline"}}</label>
|
||||
<input name="trans_gallery_subheadline" value="{{if $trans}}{{$trans.GallerySubheadline}}{{end}}" placeholder="{{$.Config.Gallery.Subheadline}}">
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{/* ── Comparison ── */}}
|
||||
{{if $.Config.Comparison.Enabled}}
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.trans_section_comparison"}}</h5>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_comparison_headline"}}</label>
|
||||
<input name="trans_comparison_headline" value="{{if $trans}}{{$trans.ComparisonHeadline}}{{end}}" placeholder="{{if $.Config.Comparison.Headline}}{{$.Config.Comparison.Headline}}{{else}}How We Compare{{end}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_comparison_subheadline"}}</label>
|
||||
<input name="trans_comparison_subheadline" value="{{if $trans}}{{$trans.ComparisonSubheadline}}{{end}}" placeholder="{{$.Config.Comparison.Subheadline}}">
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{/* ── Footer ── */}}
|
||||
{{if or $.Config.Footer.Copyright $.Config.Footer.Links}}
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.trans_section_footer"}}</h5>
|
||||
{{if $.Config.Footer.Copyright}}
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_footer_copyright"}}</label>
|
||||
<input name="trans_footer_copyright" value="{{if $trans}}{{$trans.FooterCopyright}}{{end}}" placeholder="{{$.Config.Footer.Copyright}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{range $i, $link := $.Config.Footer.Links}}
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_footer_link"}} — <em>{{$link.Label}}</em></label>
|
||||
<input name="trans_footer_link_{{$i}}" value="{{if $trans}}{{index $trans.FooterLinkLabels $i}}{{end}}" placeholder="{{$link.Label}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{/* ── SEO ── */}}
|
||||
{{if or $.Config.SEO.Title $.Config.SEO.Description}}
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.trans_section_seo"}}</h5>
|
||||
{{if $.Config.SEO.Title}}
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_seo_title"}}</label>
|
||||
<input name="trans_seo_title" value="{{if $trans}}{{$trans.SEOTitle}}{{end}}" placeholder="{{$.Config.SEO.Title}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{if $.Config.SEO.Description}}
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_seo_description"}}</label>
|
||||
<textarea name="trans_seo_description" rows="2" placeholder="{{$.Config.SEO.Description}}">{{if $trans}}{{$trans.SEODescription}}{{end}}</textarea>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{/* ── Navigation Labels ── */}}
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.trans_section_navigation"}}</h5>
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_nav_label"}} — <em>{{$.Config.Navigation.LabelValueProps}}</em></label>
|
||||
<input name="trans_nav_label_value_props" value="{{if $trans}}{{$trans.NavLabelValueProps}}{{end}}" placeholder="{{$.Config.Navigation.LabelValueProps}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_nav_label"}} — <em>{{$.Config.Navigation.LabelFeatures}}</em></label>
|
||||
<input name="trans_nav_label_features" value="{{if $trans}}{{$trans.NavLabelFeatures}}{{end}}" placeholder="{{$.Config.Navigation.LabelFeatures}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_nav_label"}} — <em>{{$.Config.Navigation.LabelBlog}}</em></label>
|
||||
<input name="trans_nav_label_blog" value="{{if $trans}}{{$trans.NavLabelBlog}}{{end}}" placeholder="{{$.Config.Navigation.LabelBlog}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_nav_label"}} — <em>{{$.Config.Navigation.LabelGallery}}</em></label>
|
||||
<input name="trans_nav_label_gallery" value="{{if $trans}}{{$trans.NavLabelGallery}}{{end}}" placeholder="{{$.Config.Navigation.LabelGallery}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_nav_label"}} — <em>{{$.Config.Navigation.LabelPricing}}</em></label>
|
||||
<input name="trans_nav_label_pricing" value="{{if $trans}}{{$trans.NavLabelPricing}}{{end}}" placeholder="{{$.Config.Navigation.LabelPricing}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_nav_label"}} — <em>{{$.Config.Navigation.LabelCompare}}</em></label>
|
||||
<input name="trans_nav_label_compare" value="{{if $trans}}{{$trans.NavLabelCompare}}{{end}}" placeholder="{{$.Config.Navigation.LabelCompare}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_nav_label"}} — <em>{{if $.Config.Navigation.LabelDocs}}{{$.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}</em></label>
|
||||
<input name="trans_nav_label_docs" value="{{if $trans}}{{$trans.NavLabelDocs}}{{end}}" placeholder="{{if $.Config.Navigation.LabelDocs}}{{$.Config.Navigation.LabelDocs}}{{else}}Docs{{end}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.trans_nav_label"}} — <em>{{if $.Config.Navigation.LabelReleases}}{{$.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}</em></label>
|
||||
<input name="trans_nav_label_releases" value="{{if $trans}}{{$trans.NavLabelReleases}}{{end}}" placeholder="{{if $.Config.Navigation.LabelReleases}}{{$.Config.Navigation.LabelReleases}}{{else}}Releases{{end}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<button class="ui primary tiny button">{{ctx.Locale.Tr "repo.settings.pages.save_translation"}}</button>
|
||||
</div>
|
||||
@@ -115,4 +365,54 @@
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var selector = document.getElementById('lang-selector');
|
||||
if (!selector) return;
|
||||
|
||||
var aiForm = document.getElementById('ai-translate-form');
|
||||
var aiLangInput = document.getElementById('ai-translate-lang');
|
||||
var deleteForm = document.getElementById('delete-translation-form');
|
||||
var deleteLangInput = document.getElementById('delete-translate-lang');
|
||||
|
||||
function showPanel() {
|
||||
var lang = selector.value;
|
||||
document.querySelectorAll('.lang-panel').forEach(function(p) { p.style.display = 'none'; });
|
||||
var panel = document.getElementById('lang-panel-' + lang);
|
||||
if (panel) panel.style.display = '';
|
||||
|
||||
// Update AI translate target
|
||||
if (aiLangInput) aiLangInput.value = lang;
|
||||
|
||||
// Update delete button target + visibility
|
||||
if (deleteLangInput) deleteLangInput.value = lang;
|
||||
if (deleteForm) {
|
||||
// Show delete button only if this language has a translation
|
||||
var hasTranslation = selector.options[selector.selectedIndex].textContent.indexOf('\u2713') !== -1;
|
||||
deleteForm.style.display = hasTranslation ? '' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
selector.addEventListener('change', showPanel);
|
||||
// Show first language on load
|
||||
showPanel();
|
||||
|
||||
// Loading spinners for AI translate buttons
|
||||
function showTranslateLoading(form) {
|
||||
if (!form) return;
|
||||
form.addEventListener('submit', function() {
|
||||
var btn = form.querySelector('button[type="submit"]');
|
||||
btn.classList.add('loading', 'disabled');
|
||||
btn.disabled = true;
|
||||
// Hide all lang panels and action bar buttons, show loader
|
||||
document.querySelectorAll('.lang-panel').forEach(function(p) { p.style.display = 'none'; });
|
||||
var loading = document.getElementById('ai-translate-loading');
|
||||
if (loading) loading.classList.remove('tw-hidden');
|
||||
});
|
||||
}
|
||||
showTranslateLoading(document.getElementById('ai-translate-form'));
|
||||
showTranslateLoading(document.getElementById('ai-translate-all-form'));
|
||||
})();
|
||||
</script>
|
||||
{{template "repo/settings/layout_footer" .}}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
{{if .PagesEnabled}}
|
||||
<div class="ui secondary pointing menu tw-mb-4">
|
||||
<a class="{{if .PageIsSettingsPagesGeneral}}active {{end}}item" href="{{.RepoLink}}/settings/pages">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.general"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsPagesBrand}}active {{end}}item" href="{{.RepoLink}}/settings/pages/brand">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.brand"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsPagesHero}}active {{end}}item" href="{{.RepoLink}}/settings/pages/hero">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.hero"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsPagesContent}}active {{end}}item" href="{{.RepoLink}}/settings/pages/content">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.content"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsPagesComparison}}active {{end}}item" href="{{.RepoLink}}/settings/pages/comparison">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.comparison"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsPagesSocial}}active {{end}}item" href="{{.RepoLink}}/settings/pages/social">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.social"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsPagesPricing}}active {{end}}item" href="{{.RepoLink}}/settings/pages/pricing">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.pricing"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsPagesFooter}}active {{end}}item" href="{{.RepoLink}}/settings/pages/footer">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.footer"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsPagesTheme}}active {{end}}item" href="{{.RepoLink}}/settings/pages/theme">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.theme"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsPagesLanguages}}active {{end}}item" href="{{.RepoLink}}/settings/pages/languages">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.languages"}}
|
||||
</a>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -97,7 +97,7 @@
|
||||
<input name="og_image" value="{{.Config.SEO.OGImage}}" placeholder="https://example.com/og-image.png">
|
||||
</div>
|
||||
<div id="og-image-preview" {{if not .Config.SEO.UseMediaKitOG}}style="display:none"{{end}}>
|
||||
<img src="{{.Repository.Link}}/social-preview" style="max-width:400px;border-radius:8px;border:1px solid #ddd;margin-top:8px;">
|
||||
<img id="og-preview-img" src="{{.Repository.Link}}/social-preview?v={{.Repository.UpdatedUnix}}" style="max-width:400px;border-radius:8px;border:1px solid #ddd;margin-top:8px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -170,6 +170,10 @@
|
||||
document.getElementById('use-media-kit-og').addEventListener('change', function() {
|
||||
document.getElementById('og-image-manual').style.display = this.checked ? 'none' : '';
|
||||
document.getElementById('og-image-preview').style.display = this.checked ? '' : 'none';
|
||||
if (this.checked) {
|
||||
const img = document.getElementById('og-preview-img');
|
||||
img.src = img.src.replace(/[?&]v=\d+/, '') + '?v=' + Date.now();
|
||||
}
|
||||
});
|
||||
|
||||
function updateColorPreview(type) {
|
||||
|
||||
Reference in New Issue
Block a user