Merge branch 'main' into devcontainer/init-shellscript
Some checks failed
DevStar Studio Auto Test Pipeline / unit-frontend-test (pull_request) Failing after 1m31s
DevStar Studio Auto Test Pipeline / unit-backend-test (pull_request) Failing after 32s
DevStar E2E Test / e2e-test (pull_request) Successful in 7m52s
DevStar Studio CI/CD Pipeline / build-and-push-x86-64-docker-image (pull_request) Failing after 5m29s

This commit is contained in:
2025-12-19 06:48:30 +00:00
11 changed files with 506 additions and 81 deletions

View File

@@ -58,6 +58,7 @@ organization = Organization
mirror = Mirror
issue_milestone = Milestone
new_repo = New Repository
new_repo_from_template = New Repository from Template
new_migrate = New Migration
new_mirror = New Mirror
new_fork = New Repository Fork

View File

@@ -56,6 +56,7 @@ organization=组织
mirror=镜像
issue_milestone=里程碑
new_repo=创建仓库
new_repo_from_template=从模板创建仓库
new_migrate=迁移外部仓库
new_mirror=创建新的镜像
new_fork=派生新仓库

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Creator: CorelDRAW X7 -->
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="41.7913mm" height="41.6859mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
viewBox="0 0 1079 1076"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<style type="text/css">
<![CDATA[
.fil1 {fill:#003CA8}
.fil0 {fill:#FF8812}
]]>
</style>
</defs>
<g id="图层_x0020_1">
<metadata id="CorelCorpID_0Corel-Layer"/>
<path class="fil0" d="M825 432l0 0c12,-2 19,13 10,21l0 0c-58,48 -84,124 -68,198l0 0c3,12 -13,20 -21,10l0 0c-45,-54 -115,-81 -185,-70 -108,58 -231,135 -303,214 -86,92 -100,229 49,248 -427,118 -464,-256 244,-492 49,-48 70,-119 55,-187l0 0c-2,-12 13,-19 21,-10l0 0c48,58 124,84 198,68z"/>
<path class="fil1" d="M636 0c102,0 187,30 253,80 75,55 127,135 158,226l0 0c11,32 19,65 24,98 5,35 8,70 7,105 0,34 -3,69 -8,103 -6,34 -14,67 -24,99 -32,94 -85,178 -160,236 -67,53 -150,84 -250,84l-267 0c-55,0 -91,-10 -112,-28 -19,-17 -25,-39 -22,-64 4,-24 15,-51 33,-78 60,-92 225,-230 320,-253 -101,59 -189,170 -231,243l279 0c57,0 105,-19 144,-51 44,-36 76,-88 95,-146 8,-23 14,-47 18,-71 4,-25 6,-50 6,-75 0,-25 -2,-50 -6,-74 -3,-24 -9,-48 -17,-70l0 0c-18,-55 -49,-103 -93,-136 -38,-30 -88,-47 -147,-47l0 0 -141 0 -68 0c-26,0 -51,4 -73,13 -22,9 -43,23 -61,42 -18,18 -32,38 -42,61 -9,22 -13,46 -13,72l0 213c135,-43 226,-51 307,-33 -160,29 -354,110 -474,221l-14 13 0 -19 0 -428 0 0 0 0c0,-94 38,-178 98,-238 61,-61 145,-98 238,-98l0 0 1 0 0 0 242 0 0 0z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
@@ -20,6 +21,7 @@ import (
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
devcontainer_service "code.gitea.io/gitea/services/devcontainer"
)
// appendPrivateInformation appends the owner and key type information to api.PublicKey
@@ -216,6 +218,15 @@ func CreateUserPublicKey(ctx *context.APIContext, form api.CreateKeyOption, uid
repo.HandleAddKeyError(ctx, err)
return
}
// 将公钥添加到所有打开的容器中(非阻塞操作,失败不影响密钥添加)
log.Info("将公钥添加到所有打开的容器中")
err = devcontainer_service.AddPublicKeyToAllRunningDevContainer(ctx, uid, content)
if err != nil {
// 记录错误但不中断流程,因为密钥已经成功添加到数据库
log.Error("AddPublicKey To Running DevContainers failed: %v", err)
}
apiLink := composePublicKeysAPILink()
apiKey := convert.ToPublicKey(apiLink, key)
if ctx.Doer.IsAdmin || ctx.Doer.ID == key.OwnerID {

View File

@@ -220,6 +220,7 @@ func GetDevContainerStatus(ctx *context.Context) {
"windsurfUrl": windsurfUrl,
})
}
func CreateDevContainerConfiguration(ctx *context.Context) {
hasDevContainerConfiguration, err := devcontainer_service.HasDevContainerConfiguration(ctx, ctx.Repo)
if err != nil {
@@ -254,6 +255,7 @@ func CreateDevContainerConfiguration(ctx *context.Context) {
}
ctx.Redirect(path.Join(ctx.Repo.RepoLink, "/devcontainer"))
}
func CreateDevContainer(ctx *context.Context) {
hasDevContainer, err := devcontainer_service.HasDevContainer(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
if err != nil {
@@ -271,6 +273,7 @@ func CreateDevContainer(ctx *context.Context) {
}
ctx.Redirect(path.Join(ctx.Repo.RepoLink, "/devcontainer"))
}
func DeleteDevContainer(ctx *context.Context) {
hasDevContainer, err := devcontainer_service.HasDevContainer(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
if err != nil {
@@ -288,6 +291,7 @@ func DeleteDevContainer(ctx *context.Context) {
}
ctx.Redirect(path.Join(ctx.Repo.RepoLink, "/devcontainer"))
}
func RestartDevContainer(ctx *context.Context) {
hasDevContainer, err := devcontainer_service.HasDevContainer(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
if err != nil {
@@ -306,6 +310,7 @@ func RestartDevContainer(ctx *context.Context) {
}
ctx.JSON(http.StatusOK, map[string]string{"status": "6"})
}
func StopDevContainer(ctx *context.Context) {
hasDevContainer, err := devcontainer_service.HasDevContainer(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
if err != nil {
@@ -323,6 +328,7 @@ func StopDevContainer(ctx *context.Context) {
}
ctx.JSON(http.StatusOK, map[string]string{"status": "7"})
}
func UpdateDevContainer(ctx *context.Context) {
hasDevContainer, err := devcontainer_service.HasDevContainer(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
if err != nil {
@@ -349,6 +355,7 @@ func UpdateDevContainer(ctx *context.Context) {
}
ctx.JSON(http.StatusOK, map[string]string{"redirect": ctx.Repo.RepoLink + "/devcontainer", "message": "成功"})
}
func GetTerminalCommand(ctx *context.Context) {
// 设置 CORS 响应头
ctx.Resp.Header().Set("Access-Control-Allow-Origin", "*")

View File

@@ -0,0 +1,20 @@
package repo
import (
"net/http"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/services/context"
)
const (
tplCreateFromTemplate templates.TplName = "repo/create-from-template/create-from-template"
)
func CreateFromTemplate(ctx *context.Context) {
createCommon(ctx)
if ctx.Written() {
return
}
ctx.HTML(http.StatusOK, tplCreateFromTemplate)
}

View File

@@ -1066,6 +1066,8 @@ func registerWebRoutes(m *web.Router) {
m.Group("/repo", func() {
m.Get("/create", repo.Create)
m.Post("/create", web.Bind(forms.CreateRepoForm{}), repo.CreatePost)
m.Get("/create-from-template", repo.CreateFromTemplate)
// m.Get("/create-from-template", repo.Create)
m.Get("/migrate", repo.Migrate)
m.Post("/migrate", web.Bind(forms.MigrateRepoForm{}), repo.MigratePost)
m.Get("/search", repo.SearchRepo)

View File

@@ -72,6 +72,9 @@
<div class="menu">
<a class="item" href="{{AppSubUrl}}/repo/create">
{{svg "octicon-plus"}} {{ctx.Locale.Tr "new_repo"}}
</a>
<a class="item" href="{{AppSubUrl}}/repo/create-from-template">
{{svg "octicon-plus-circle"}} {{ctx.Locale.Tr "new_repo_from_template"}}
</a>
{{if not .DisableMigrations}}
<a class="item" href="{{AppSubUrl}}/repo/migrate">

View File

@@ -0,0 +1,307 @@
{{template "base/head" .}}
<!-- 强制加载 jQuery 和 Semantic UI -->
<script>
if (typeof jQuery === 'undefined') {
document.write('<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.min.js"><\/script>');
document.write('<script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.5.0/dist/semantic.min.js"><\/script>');
}
</script>
<style>
.search-sort-container {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 20px;
}
.search-input-container {
flex: 1;
min-width: 0;
}
.sort-dropdown-container {
white-space: nowrap;
flex-shrink: 0;
}
.sort-dropdown-container:hover {
cursor: pointer;
background: var(--color-hover) !important;
}
.sort-dropdown-container.active {
background: var(--color-active) !important;
}
.sort-dropdown-trigger {
padding: 10px 12px;
}
.ui.dropdown .menu {
position: absolute;
top: 110%;
}
.ui.dropdown .text {
display: inline-block;
max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
}
/* 新增样式:选中项高亮 */
.ui.dropdown .menu .active.item,
.ui.dropdown .menu .item:hover {
background: var(--color-hover) !important;
}
/* 当前选中项样式 */
.ui.dropdown .menu .selected.item {
color: var(--color-text) !important;
background: var(--color-active) !important;
font-weight: var(--font-weight-medium) !important;
}
</style>
<div role="main" aria-label="{{.Title}}" class="page-content repository new migrate">
<div class="ui middle very relaxed page grid">
<div class="column">
<!-- 搜索和排序控制栏 -->
<div class="search-sort-container">
<!-- 搜索框 -->
<div class="ui action input search-input-container">
<input type="text" id="searchInput" placeholder="搜索模板...">
<button class="ui small icon button" id="searchButton" aria-label="{{ctx.Locale.Tr " search.search"}}" {{with
.Tooltip}}data-tooltip-content="{{.}}" {{end}}{{if .Disabled}} disabled{{end}}>{{svg
"octicon-search"}}</button>
</div>
<!-- 排序下拉框 -->
<div class="sort-dropdown-container">
<div class="ui small dropdown type jump item" id="sortDropdown">
<div class="sort-dropdown-trigger">
<span class="text">
{{ctx.Locale.Tr "repo.issues.filter_sort"}}
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</div>
<div class="menu">
<div class="item " data-value="newest">最新创建</div>
<div class="item" data-value="oldest">最早创建</div>
<div class="item" data-value="name_asc">按字母顺序排序</div>
<div class="item" data-value="name_desc">按字母逆序排序</div>
<div class="item" data-value="recently_updated">最近更新</div>
<div class="item" data-value="least_recently_updated">最早更新</div>
<div class="item" data-value="most_likes">点赞由多到少</div>
<div class="item" data-value="least_likes">点赞由少到多</div>
<div class="item" data-value="most_forks">派生由多到少</div>
<div class="item" data-value="least_forks">派生由少到多</div>
</div>
</div>
</div>
</div>
<!-- 模板卡片容器 -->
<div class="ui cards migrate-entries" id="template-cards">
<div class="ui active inverted">
<div class="ui text loader">加载模板中...</div>
</div>
</div>
</div>
</div>
</div>
<script>
// 确保 jQuery 已加载
function ensureJQuery(callback) {
if (window.jQuery) {
callback();
} else {
setTimeout(() => ensureJQuery(callback), 100);
}
}
document.addEventListener('DOMContentLoaded', function () {
ensureJQuery(function () {
// 初始化变量
const cardsContainer = document.getElementById('template-cards');
const searchInput = document.getElementById('searchInput');
const searchButton = document.getElementById('searchButton');
const sortContainer = document.querySelector('.sort-dropdown-container');
const sortTrigger = document.querySelector('.sort-dropdown-trigger');
const sortDropdown = $('#sortDropdown');
let allRepos = [];
let currentSearchTerm = '';
let currentSortValue = 'newest';
// 全局设置silent模式
$.fn.dropdown.settings.silent = true;
$.fn.transition.settings.silent = true;
// 初始化下拉框并添加选中状态处理
sortDropdown.dropdown({
action: 'hide',
onShow: function () {
$(`.ui.dropdown .menu .item[data-value="${currentSortValue}"]`).addClass('selected');
return document.body.contains(this);
},
onHide: function () {
return document.body.contains(this);
},
onChange: function (value) {
currentSortValue = value;
$('.ui.dropdown .menu .item').removeClass('selected');
$(`.ui.dropdown .menu .item[data-value="${value}"]`).addClass('selected');
sortContainer.classList.remove('active');
applyFiltersAndRender();
},
onInitialize: function () {
$('.ui.dropdown .menu .item').removeClass('selected');
$(`.ui.dropdown .menu .item[data-value="newest"]`).addClass('selected');
}
});
// 点击触发器时切换 active 类
sortTrigger.addEventListener('click', function () {
sortContainer.classList.toggle('active');
$(`.ui.dropdown .menu .item[data-value="${currentSortValue}"]`).addClass('selected');
});
// 点击页面其他位置时移除 active 类
document.addEventListener('click', function (event) {
if (!sortContainer.contains(event.target)) {
sortContainer.classList.remove('active');
}
});
//鼠标右键时移除active类
document.addEventListener('contextmenu', function (event) {
if (!sortContainer.contains(event.target)) {
sortContainer.classList.remove('active');
}
});
// 获取模板数据
fetch('https://devstar.cn/api/v1/repos/search?template=true')
.then(response => response.json())
.then(data => {
allRepos = (data.data || []).map(repo => ({
...repo,
full_name: "Devstar.cn/" + repo.full_name,
createdTimestamp: new Date(repo.created_at).getTime(),
updatedTimestamp: new Date(repo.updated_at).getTime(),
lowerName: repo.name.toLowerCase(),
lowerDescription: (repo.description || '').toLowerCase(),
starsCount: repo.stars_count || 0,
forksCount: repo.forks_count || 0
}));
applyFiltersAndRender();
// 搜索按钮点击事件
searchButton.addEventListener('click', function () {
currentSearchTerm = searchInput.value.trim().toLowerCase();
applyFiltersAndRender();
});
// 输入框回车事件
searchInput.addEventListener('keypress', function (e) {
if (e.key === 'Enter') {
currentSearchTerm = searchInput.value.trim().toLowerCase();
applyFiltersAndRender();
}
});
})
.catch(error => {
console.error('获取模板数据失败:', error);
cardsContainer.innerHTML = '<div class="ui error message">加载模板失败</div>';
});
// 应用搜索和排序筛选
function applyFiltersAndRender() {
// 1. 应用搜索筛选
let filteredRepos = currentSearchTerm ?
allRepos.filter(repo =>
repo.lowerName.includes(currentSearchTerm)
) :
[...allRepos];
// 2. 应用排序
let sortedRepos = [...filteredRepos];
switch (currentSortValue) {
case 'newest':
sortedRepos.sort((a, b) => b.createdTimestamp - a.createdTimestamp);
break;
case 'oldest':
sortedRepos.sort((a, b) => a.createdTimestamp - b.createdTimestamp);
break;
case 'name_asc':
sortedRepos.sort((a, b) => a.name.localeCompare(b.name));
break;
case 'name_desc':
sortedRepos.sort((a, b) => b.name.localeCompare(a.name));
break;
case 'recently_updated':
sortedRepos.sort((a, b) => b.updatedTimestamp - a.updatedTimestamp);
break;
case 'least_recently_updated':
sortedRepos.sort((a, b) => a.updatedTimestamp - b.updatedTimestamp);
break;
case 'most_likes':
sortedRepos.sort((a, b) => b.starsCount - a.starsCount);
break;
case 'least_likes':
sortedRepos.sort((a, b) => a.starsCount - b.starsCount);
break;
case 'most_forks':
sortedRepos.sort((a, b) => b.forksCount - a.forksCount);
break;
case 'least_forks':
sortedRepos.sort((a, b) => a.forksCount - b.forksCount);
break;
}
// 3. 渲染结果
renderTemplates(sortedRepos);
}
// 渲染模板列表
function renderTemplates(repos) {
cardsContainer.innerHTML = '';
if (repos && repos.length > 0) {
repos.forEach(repo => {
const createdDate = new Date(repo.owner?.created || repo.created_at);
const formattedDate = createdDate.toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
const card = `
<div class="ui card migrate-entry">
<a class="ui card migrate-entry tw-flex tw-items-center"
href="{{.AppSubUrl}}/repo/create?template_name=${repo.name}&description=${repo.clone_url}&template_full_name=${repo.full_name}">
<img src="/assets/img/favicon.svg" width="184" height="184" class="tw-p-4" alt="${repo.name}" />
<div class="content">
<div class="header tw-text-center">${repo.name}</div>
<div class="description tw-text-center tw-text-balance">${repo.description}</div>
</div>
</a>
</div>
`;
cardsContainer.insertAdjacentHTML('beforeend', card);
});
} else {
cardsContainer.innerHTML = '<div class="ui warning message">未找到匹配的模板</div>';
}
// 确保下拉框状态正确
sortDropdown.dropdown('refresh');
}
});
});
</script>
{{template "base/footer" .}}

View File

@@ -1,4 +1,6 @@
{{template "base/head" .}}
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.js"></script>
<div role="main" aria-label="{{.Title}}" class="page-content repository new-repo">
<div class="ui container medium-width">
<h3 class="ui top attached header">
@@ -17,19 +19,17 @@
<span class="text"></span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
<div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}"
{{if not .CanCreateRepoInDoer}}
data-create-repo-disallowed-prompt="{{ctx.Locale.TrN .MaxCreationLimit "repo.form.reach_limit_of_creation_1" "repo.form.reach_limit_of_creation_n" .MaxCreationLimitOfDoer}}"
{{end}}
>
<div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}" {{if not
.CanCreateRepoInDoer}} data-create-repo-disallowed-prompt="{{ctx.Locale.TrN .MaxCreationLimit "repo.form.reach_limit_of_creation_1" "repo.form.reach_limit_of_creation_n" .MaxCreationLimitOfDoer}}"
{{end}}>
{{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
{{.SignedUser.ShortName 40}}
</div>
{{range .Orgs}}
<div class="item" data-value="{{.ID}}" title="{{.Name}}">
{{ctx.AvatarUtils.Avatar . 28 "mini"}}
{{.ShortName 40}}
</div>
<div class="item" data-value="{{.ID}}" title="{{.Name}}">
{{ctx.AvatarUtils.Avatar . 28 "mini"}}
{{.ShortName 40}}
</div>
{{end}}
</div>
</div>
@@ -40,19 +40,21 @@
<label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
<input id="repo_name" name="repo_name" value="{{.repo_name}}" autofocus required maxlength="100">
<span class="help" data-help-for-repo-name>{{ctx.Locale.Tr "repo.repo_name_helper"}}</span>
<span class="help tw-hidden" data-help-for-repo-name=".profile">{{ctx.Locale.Tr "repo.repo_name_profile_public_hint"}}</span>
<span class="help tw-hidden" data-help-for-repo-name=".profile-private">{{ctx.Locale.Tr "repo.repo_name_profile_private_hint"}}</span>
<span class="help tw-hidden" data-help-for-repo-name=".profile">{{ctx.Locale.Tr
"repo.repo_name_profile_public_hint"}}</span>
<span class="help tw-hidden" data-help-for-repo-name=".profile-private">{{ctx.Locale.Tr
"repo.repo_name_profile_private_hint"}}</span>
</div>
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.visibility"}}</label>
<div class="ui checkbox">
{{if .IsForcedPrivate}}
<input name="private" type="checkbox" checked disabled>
<label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
<input name="private" type="checkbox" checked disabled>
<label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
{{else}}
<input name="private" type="checkbox" {{if .private}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
<input name="private" type="checkbox" {{if .private}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
{{end}}
</div>
<span class="help">{{ctx.Locale.Tr "repo.visibility_description"}}</span>
@@ -63,73 +65,73 @@
</div>
{{if .devstar_template_name}}
<div id="devstar_template_area" class="inline field">
<label>{{ctx.Locale.Tr "repo.devstar_template"}}</label>
<div id="devstar_template_search" class="ui search selection dropdown">
<input type="hidden" id="devstar_template" name="devstar_template" value="">
<div id="devstar_template_name" class="default text">{{.devstar_template_name}}</div>
<div class="menu"></div>
</div>
<span class="help">{{ctx.Locale.Tr "repo.devstar_template_desc" "https://DevStar.cn/"}}</span>
<div id="devstar_template_area" class="inline field">
<label>{{ctx.Locale.Tr "repo.devstar_template"}}</label>
<div id="devstar_template_search" class="ui search selection dropdown">
<input type="hidden" id="devstar_template" name="devstar_template" value="">
<div id="devstar_template_name" class="default text">{{.devstar_template_name}}</div>
<div class="menu"></div>
</div>
<span class="help">{{ctx.Locale.Tr "repo.devstar_template_desc" "https://DevStar.cn/"}}</span>
</div>
{{end}}
<div id="repo_template_area">
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.template"}}</label>
<div id="repo_template_search" class="ui search selection dropdown">
<input type="hidden" id="repo_template" name="repo_template" value="{{or .repo_template ""}}">
<div id="repo_template_name" class="default text">{{.repo_template_name}}</div>
<div class="menu">
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.template"}}</label>
<div id="repo_template_search" class="ui search selection dropdown">
<input type="hidden" id="repo_template" name="repo_template" value="{{or .repo_template ""}}">
<div id="repo_template_name" class="default text">{{.repo_template_name}}</div>
<div class="menu">
</div>
</div>
</div>
<div id="template_units" {{if not .repo_template}}class="tw-hidden" {{end}}>
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.template.items"}}</label>
<div class="ui checkbox">
<input name="git_content" type="checkbox" {{if .git_content}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.template.git_content"}}</label>
</div>
<div class="ui checkbox" {{if not .SignedUser.CanEditGitHook}}data-tooltip-content="{{ctx.Locale.Tr "repo.template.git_hooks_tooltip"}}"{{end}}>
<input name="git_hooks" type="checkbox" {{if .git_hooks}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.template.git_hooks"}}</label>
</div>
</div>
<div class="inline field">
<label></label>
<div class="ui checkbox">
<input name="webhooks" type="checkbox" {{if .webhooks}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.template.webhooks"}}</label>
</div>
<div class="ui checkbox">
<input name="topics" type="checkbox" {{if .topics}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.template.topics"}}</label>
</div>
</div>
<div class="inline field">
<label></label>
<div class="ui checkbox">
<input name="avatar" type="checkbox" {{if .avatar}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.template.avatar"}}</label>
</div>
<div class="ui checkbox">
<input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.template.issue_labels"}}</label>
</div>
</div>
<div class="inline field">
<label></label>
<div class="ui checkbox">
<input name="protected_branch" type="checkbox" {{if .protected_branch}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.protected_branch"}}</label>
</div>
</div>
</div>
</div>
<div id="template_units" {{if not .repo_template}}class="tw-hidden"{{end}}>
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.template.items"}}</label>
<div class="ui checkbox">
<input name="git_content" type="checkbox" {{if .git_content}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.template.git_content"}}</label>
</div>
<div class="ui checkbox" {{if not .SignedUser.CanEditGitHook}}data-tooltip-content="{{ctx.Locale.Tr "repo.template.git_hooks_tooltip"}}"{{end}}>
<input name="git_hooks" type="checkbox" {{if .git_hooks}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.template.git_hooks"}}</label>
</div>
</div>
<div class="inline field">
<label></label>
<div class="ui checkbox">
<input name="webhooks" type="checkbox" {{if .webhooks}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.template.webhooks"}}</label>
</div>
<div class="ui checkbox">
<input name="topics" type="checkbox" {{if .topics}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.template.topics"}}</label>
</div>
</div>
<div class="inline field">
<label></label>
<div class="ui checkbox">
<input name="avatar" type="checkbox" {{if .avatar}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.template.avatar"}}</label>
</div>
<div class="ui checkbox">
<input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.template.issue_labels"}}</label>
</div>
</div>
<div class="inline field">
<label></label>
<div class="ui checkbox">
<input name="protected_branch" type="checkbox" {{if .protected_branch}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.protected_branch"}}</label>
</div>
</div>
</div>
</div>
<div id="non_template" {{if .repo_template}}class="tw-hidden"{{end}}>
<div id="non_template" {{if .repo_template}}class="tw-hidden" {{end}}>
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.issue_labels"}}</label>
<div class="ui search selection dropdown">
@@ -138,7 +140,7 @@
<div class="menu">
<div class="item" data-value="">{{ctx.Locale.Tr "repo.issue_labels_helper"}}</div>
{{range .LabelTemplateFiles}}
<div class="item" data-value="{{.DisplayName}}">{{.DisplayName}}<br><i>({{.Description}})</i></div>
<div class="item" data-value="{{.DisplayName}}">{{.DisplayName}}<br><i>({{.Description}})</i></div>
{{end}}
</div>
</div>
@@ -153,7 +155,7 @@
<div class="default text">{{ctx.Locale.Tr "repo.repo_gitignore_helper"}}</div>
<div class="menu">
{{range .Gitignores}}
<div class="item" data-value="{{.}}">{{.}}</div>
<div class="item" data-value="{{.}}">{{.}}</div>
{{end}}
</div>
</div>
@@ -167,7 +169,7 @@
<div class="menu">
<div class="item" data-value="">{{ctx.Locale.Tr "repo.license_helper"}}</div>
{{range .Licenses}}
<div class="item" data-value="{{.}}">{{.}}</div>
<div class="item" data-value="{{.}}">{{.}}</div>
{{end}}
</div>
</div>
@@ -181,7 +183,7 @@
<div class="default text">{{ctx.Locale.Tr "repo.readme_helper"}}</div>
<div class="menu">
{{range .Readmes}}
<div class="item" data-value="{{.}}">{{.}}</div>
<div class="item" data-value="{{.}}">{{.}}</div>
{{end}}
</div>
</div>
@@ -196,17 +198,19 @@
</div>
<div class="inline field">
<label for="default_branch">{{ctx.Locale.Tr "repo.default_branch"}}</label>
<input id="default_branch" name="default_branch" value="{{.default_branch}}" placeholder="{{.default_branch}}">
<input id="default_branch" name="default_branch" value="{{.default_branch}}"
placeholder="{{.default_branch}}">
<span class="help">{{ctx.Locale.Tr "repo.default_branch_helper"}}</span>
</div>
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.object_format"}}</label>
<div class="ui selection owner dropdown">
<input type="hidden" id="object_format_name" name="object_format_name" value="{{or .object_format_name .DefaultObjectFormat.Name}}" required>
<input type="hidden" id="object_format_name" name="object_format_name"
value="{{or .object_format_name .DefaultObjectFormat.Name}}" required>
<div class="default text">{{.DefaultObjectFormat.Name}}</div>
<div class="menu">
{{range .SupportedObjectFormats}}
<div class="item" data-value="{{.Name}}">{{.Name}}</div>
<div class="item" data-value="{{.Name}}">{{.Name}}</div>
{{end}}
</div>
</div>
@@ -231,4 +235,52 @@
</div>
</div>
</div>
<script>
$(document).ready(function () {
// 使用 jQuery 的 hide/show 方法替代 hideElem
const hideElem = function (elem) {
$(elem).hide();
};
const showElem = function (elem) {
$(elem).show();
};
// 处理DevStar模板选择变化
const $devstarTemplate = $('#devstar_template');
const $repoTemplate = $('#repo_template');
const $gitURLTemplateArea = $('#git_url_template_area');
const $repoTemplateArea = $('#repo_template_area');
const $templateUnits = $('#template_units');
const $nonTemplate = $('#non_template');
const redirectDevStarTemplate = function () {
hideElem($gitURLTemplateArea);
hideElem($repoTemplateArea);
hideElem($nonTemplate);
};
// 从URL参数获取值并填充表单
const urlParams = new URLSearchParams(window.location.search);
const templateName = urlParams.get('template_name');
const templateFullName = urlParams.get('template_full_name');
const description = urlParams.get('description');
if (templateName) {
$('#repo_name').val(templateName);
}
if (description) {
$('#description').val("init repo from " + description);
$('#devstar_template_search').dropdown('set value', description);
}
if (templateFullName) {
$('#devstar_template_search').dropdown('set text', templateFullName);
}
// 如果有模板参数,模拟选择模板的行为
if (templateName) {
redirectDevStarTemplate();
}
});
</script>
{{template "base/footer" .}}

View File

@@ -63,6 +63,7 @@
<div style=" display: none;" id="vsTerminal" class="item"><a class="flex-text-inline" style="color:black; " onclick="window.location.href = '{{.VSCodeUrl}}'">{{svg "octicon-code" 14}}open with VSCode</a ></div>
<div style=" display: none;" id="cursorTerminal" class="item"><a class="flex-text-inline" style="color:black; " onclick="window.location.href = '{{.CursorUrl}}'">{{svg "octicon-code" 14}}open with Cursor</a ></div>
<div style=" display: none;" id="windsurfTerminal" class="item"><a class="flex-text-inline" style="color:black;" onclick="window.location.href = '{{.WindsurfUrl}}'">{{svg "octicon-code" 14}}open with Windsurf</a ></div>
{{end}}
{{if .ValidateDevContainerConfiguration}}