Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c5daac3366 | |||
| 916211004d | |||
| 02fdc1a194 | |||
| 1b0bba09b9 | |||
| 0c0d1c1493 | |||
| 9461599b57 | |||
| 414560f470 | |||
| b43345986a | |||
| 7fbbd26b20 | |||
| b26bf4bfe8 |
@@ -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=
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -1533,10 +1533,6 @@ func PagesAdvancedPost(ctx *context.Context) {
|
||||
// Parse remaining fields
|
||||
config.Advanced.CustomCSS = ctx.FormString("custom_css")
|
||||
config.Advanced.CustomHead = ctx.FormString("custom_head")
|
||||
config.Advanced.GooglePlayID = ctx.FormString("google_play_id")
|
||||
config.Advanced.AppStoreID = ctx.FormString("app_store_id")
|
||||
config.Advanced.PublicReleases = ctx.FormBool("public_releases")
|
||||
config.Advanced.HideMobileReleases = ctx.FormBool("hide_mobile_releases")
|
||||
|
||||
if err := savePagesLandingConfig(ctx, config); err != nil {
|
||||
ctx.ServerError("SavePagesConfig", err)
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<details class="item toggleable-item" {{if or .PageIsSettingsPagesGeneral .PageIsSettingsPagesBrand .PageIsSettingsPagesHero .PageIsSettingsPagesContent .PageIsSettingsPagesComparison .PageIsSettingsPagesSocial .PageIsSettingsPagesPricing .PageIsSettingsPagesFooter .PageIsSettingsPagesTheme .PageIsSettingsPagesLanguages}}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">
|
||||
@@ -86,6 +86,9 @@
|
||||
<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}}>
|
||||
|
||||
@@ -57,33 +57,6 @@
|
||||
<textarea name="custom_head" rows="4" placeholder="<meta ...>">{{.Config.Advanced.CustomHead}}</textarea>
|
||||
</div>
|
||||
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.pages.app_stores"}}</h5>
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
<label>Google Play ID</label>
|
||||
<input name="google_play_id" value="{{.Config.Advanced.GooglePlayID}}" placeholder="com.example.app">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>App Store ID</label>
|
||||
<input name="app_store_id" value="{{.Config.Advanced.AppStoreID}}" placeholder="123456789">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inline fields">
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="public_releases" {{if .Config.Advanced.PublicReleases}}checked{{end}}>
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.public_releases"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="hide_mobile_releases" {{if .Config.Advanced.HideMobileReleases}}checked{{end}}>
|
||||
<label>{{ctx.Locale.Tr "repo.settings.pages.hide_mobile_releases"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<button class="ui primary button">{{ctx.Locale.Tr "save"}}</button>
|
||||
</div>
|
||||
|
||||
@@ -1,37 +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>
|
||||
<a class="{{if .PageIsSettingsPagesAdvanced}}active {{end}}item" href="{{.RepoLink}}/settings/pages/advanced">
|
||||
{{ctx.Locale.Tr "repo.settings.pages.advanced"}}
|
||||
</a>
|
||||
</div>
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user