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
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:
@@ -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
|
||||
|
||||
@@ -56,6 +56,7 @@ organization=组织
|
||||
mirror=镜像
|
||||
issue_milestone=里程碑
|
||||
new_repo=创建仓库
|
||||
new_repo_from_template=从模板创建仓库
|
||||
new_migrate=迁移外部仓库
|
||||
new_mirror=创建新的镜像
|
||||
new_fork=派生新仓库
|
||||
|
||||
20
public/assets/img/svg/devstar-favicon.svg
generated
Normal file
20
public/assets/img/svg/devstar-favicon.svg
generated
Normal 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 |
@@ -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 {
|
||||
|
||||
@@ -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", "*")
|
||||
|
||||
20
routers/web/repo/create_from_template.go
Normal file
20
routers/web/repo/create_from_template.go
Normal 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)
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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">
|
||||
|
||||
307
templates/repo/create-from-template/create-from-template.tmpl
Normal file
307
templates/repo/create-from-template/create-from-template.tmpl
Normal 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" .}}
|
||||
@@ -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" .}}
|
||||
|
||||
@@ -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}}
|
||||
|
||||
Reference in New Issue
Block a user