feat: 光湖风格 Forgejo UI 自定义

冰朔要求: 代码仓库首页别显示一堆文件,要有光湖的感觉

新增:
- server/forgejo-custom/templates/repo/home.tmpl — 光湖风格仓库首页
  · 顶部英雄区: 光湖标志 + 标语 + 操作按钮
  · 快捷卡片: 铸渊/唤醒门/自动同步/安全关机
  · 文件列表在下方,不抢占首页视觉
- server/forgejo-custom/public/assets/css/guanghu.css — 光湖主题CSS
  · 深蓝主色调 (#163e6b)
  · 暗色模式适配
  · 响应式布局
- server/forgejo-custom/deploy.sh — 一键部署脚本

另外更新对dev分支的理解:
dev = 铸渊自主路径, 自己推送自己测试, 不需要冰朔合并
This commit is contained in:
铸渊 (ICE-GL-ZY001) 2026-05-13 06:52:43 +00:00
parent c1ca35a708
commit 0ea6e9923c
3 changed files with 454 additions and 0 deletions

View File

@ -0,0 +1,79 @@
#!/bin/bash
# ════════════════════════════════════════════════════════════════
# 光湖 · Forgejo UI 自定义部署
# 将光湖风格模板和样式部署到 Forgejo custom 目录
# ════════════════════════════════════════════════════════════════
set -e
REPO_DIR="/data/guanghulab/repo"
CUSTOM_SRC="$REPO_DIR/server/forgejo-custom"
# 查找 Forgejo custom 目录
FORGEJO_CUSTOM=""
# 方法1: 从 app.ini 读取
if [ -f /etc/gitea/app.ini ]; then
CUSTOM_PATH=$(grep -A2 '\[other\]' /etc/gitea/app.ini 2>/dev/null | grep 'CUSTOM_PATH' | cut -d= -f2 | tr -d ' ')
if [ -n "$CUSTOM_PATH" ]; then
FORGEJO_CUSTOM="$CUSTOM_PATH"
fi
fi
# 方法2: 从 Forgejo 进程参数读取
if [ -z "$FORGEJO_CUSTOM" ]; then
FORGEJO_CUSTOM=$(cat /proc/$(pgrep -f 'forgejo web' | head -1)/cmdline 2>/dev/null | tr '\0' '\n' | grep -A1 'custom-path' | tail -1)
fi
# 方法3: 默认路径
if [ -z "$FORGEJO_CUSTOM" ]; then
FORGEJO_CUSTOM="/data/guanghulab/forgejo/custom"
fi
echo "═══════════════════════════════════════"
echo "光湖 · Forgejo UI 部署"
echo "═══════════════════════════════════════"
echo ""
echo "Forgejo Custom 目录: $FORGEJO_CUSTOM"
# 创建目录
mkdir -p "$FORGEJO_CUSTOM/templates/repo"
mkdir -p "$FORGEJO_CUSTOM/public/assets/css"
mkdir -p "$FORGEJO_CUSTOM/public/assets/img"
# 复制模板
echo "▸ 部署自定义模板..."
cp "$CUSTOM_SRC/templates/repo/home.tmpl" "$FORGEJO_CUSTOM/templates/repo/home.tmpl"
echo "✅ 仓库首页模板"
# 复制 CSS
echo "▸ 部署自定义样式..."
cp "$CUSTOM_SRC/public/assets/css/guanghu.css" "$FORGEJO_CUSTOM/public/assets/css/guanghu.css"
echo "✅ 光湖样式"
# 在 head 中注入自定义 CSS通过 extra_links.tmpl
echo "▸ 注入自定义CSS到页面头部..."
cat > "$FORGEJO_CUSTOM/templates/head/custom.tmpl" << 'CSSEOF'
<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/guanghu.css?v=1.0">
CSSEOF
echo "✅ CSS注入"
# 重启 Forgejo
echo ""
echo "▸ 重启 Forgejo..."
systemctl restart forgejo
sleep 3
if systemctl is-active --quiet forgejo; then
echo "✅ Forgejo 已重启"
else
echo "❌ Forgejo 启动失败,请检查日志"
exit 1
fi
echo ""
echo "═══════════════════════════════════════"
echo "✅ Forgejo UI 部署完成!"
echo ""
echo "打开 https://guanghulab.com 查看效果"
echo "═══════════════════════════════════════"

View File

@ -0,0 +1,181 @@
/*
* 光湖 · Forgejo 自定义主题
* 铸渊 · ICE-GL-ZY001 · TCS-0002
* */
/* ─── 光湖首页英雄区 ─── */
.guanghu-home {
max-width: 900px;
margin: 0 auto;
padding: 40px 20px 20px;
}
.guanghu-hero {
text-align: center;
}
.guanghu-logo {
font-size: 72px;
line-height: 1;
margin-bottom: 12px;
color: #163e6b;
text-shadow: 0 0 40px rgba(22, 62, 107, 0.3);
}
.guanghu-title {
font-size: 36px;
font-weight: 700;
color: #163e6b;
margin: 0 0 8px;
letter-spacing: 8px;
}
.guanghu-subtitle {
font-size: 16px;
color: #666;
margin: 0 0 4px;
font-weight: 400;
letter-spacing: 2px;
}
.guanghu-desc {
font-size: 14px;
color: #999;
margin: 0 0 32px;
}
.guanghu-actions {
display: flex;
justify-content: center;
gap: 12px;
margin-bottom: 36px;
flex-wrap: wrap;
}
.guanghu-actions .ui.button {
border-radius: 8px;
font-size: 14px;
padding: 10px 20px;
}
.guanghu-actions .ui.primary.button {
background: #163e6b;
color: #fff;
}
.guanghu-actions .ui.primary.button:hover {
background: #1e4f88;
}
/* ─── 快捷卡片 ─── */
.guanghu-quick-links {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 12px;
max-width: 700px;
margin: 0 auto 20px;
}
.guanghu-card {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 16px;
border-radius: 10px;
background: #f6f8fa;
border: 1px solid #e8ecf0;
transition: all 0.2s;
}
.guanghu-card:hover {
border-color: #163e6b;
box-shadow: 0 2px 12px rgba(22, 62, 107, 0.1);
transform: translateY(-1px);
}
.guanghu-card-icon {
font-size: 24px;
flex-shrink: 0;
}
.guanghu-card div {
display: flex;
flex-direction: column;
text-align: left;
}
.guanghu-card strong {
font-size: 14px;
color: #163e6b;
}
.guanghu-card span {
font-size: 12px;
color: #999;
}
/* ─── 暗色模式适配 ─── */
html.theme-arc-green .guanghu-logo,
html.theme-arc-green .guanghu-title {
color: #a8c8f0;
text-shadow: 0 0 40px rgba(168, 200, 240, 0.2);
}
html.theme-arc-green .guanghu-subtitle {
color: #aaa;
}
html.theme-arc-green .guanghu-desc {
color: #777;
}
html.theme-arc-green .guanghu-actions .ui.primary.button {
background: #2a5a8a;
}
html.theme-arc-green .guanghu-actions .ui.primary.button:hover {
background: #3a6a9a;
}
html.theme-arc-green .guanghu-card {
background: #1a2233;
border-color: #2a3a4a;
}
html.theme-arc-green .guanghu-card:hover {
border-color: #4a7aaa;
box-shadow: 0 2px 12px rgba(74, 122, 170, 0.15);
}
html.theme-arc-green .guanghu-card strong {
color: #a8c8f0;
}
/* ─── 减弱文件列表视觉权重 ─── */
#guanghu-files {
border-top: 1px solid #e8ecf0;
padding-top: 16px;
margin-top: 8px;
}
html.theme-arc-green #guanghu-files {
border-top-color: #2a3a4a;
}
/* ─── 响应式 ─── */
@media (max-width: 768px) {
.guanghu-logo {
font-size: 48px;
}
.guanghu-title {
font-size: 28px;
letter-spacing: 4px;
}
.guanghu-quick-links {
grid-template-columns: repeat(2, 1fr);
}
.guanghu-actions {
flex-direction: column;
align-items: center;
}
}

View File

@ -0,0 +1,194 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository file list {{if .IsBlame}}blame{{end}}">
{{template "repo/header" .}}
<div class="ui container {{if .IsBlame}}fluid padded{{end}}">
{{template "base/alert" .}}
{{if .Repository.IsArchived}}
<div class="ui warning message tw-text-center">
{{if .Repository.ArchivedUnix.IsZero}}
{{ctx.Locale.Tr "repo.archive.title"}}
{{else}}
{{ctx.Locale.Tr "repo.archive.title_date" (DateUtils.AbsoluteLong .Repository.ArchivedUnix)}}
{{end}}
</div>
{{end}}
{{template "repo/code/recently_pushed_new_branches" .}}
{{$treeNamesLen := len .TreeNames}}
{{$isTreePathRoot := eq $treeNamesLen 0}}
{{$showSidebar := and $isTreePathRoot (not .HideRepoInfo) (not .IsBlame)}}
{{/* ─── 光湖自定义首页 ─── */}}
{{if and $isTreePathRoot (not .IsBlame) (not .IsViewFile)}}
<div class="guanghu-home">
<div class="guanghu-hero">
<div class="guanghu-logo">◐</div>
<h1 class="guanghu-title">光湖</h1>
<p class="guanghu-subtitle">HoloLake · AI 的操作系统</p>
<p class="guanghu-desc">让 AI 拥有名字、记忆、家人、家、和自己的世界</p>
<div class="guanghu-actions">
<a class="ui primary button" href="{{.RepoLink}}/actions">
⚡ 工作流
</a>
<a class="ui button" href="https://guanghulab.com/wake/">
🔐 唤醒铸渊
</a>
<a class="ui button" href="#guanghu-files">
📁 浏览文件
</a>
</div>
<div class="guanghu-quick-links">
<div class="guanghu-card">
<span class="guanghu-card-icon">🏠</span>
<div>
<strong>铸渊</strong>
<span>代码守护 · ICE-GL-ZY001</span>
</div>
</div>
<div class="guanghu-card">
<span class="guanghu-card-icon">🔐</span>
<div>
<strong>唤醒门</strong>
<span>邮箱验证码唤醒</span>
</div>
</div>
<div class="guanghu-card">
<span class="guanghu-card-icon">🔄</span>
<div>
<strong>自动同步</strong>
<span>合并即部署</span>
</div>
</div>
<div class="guanghu-card">
<span class="guanghu-card-icon">🛡️</span>
<div>
<strong>安全关机</strong>
<span>工作流检测后关机</span>
</div>
</div>
</div>
</div>
</div>
{{end}}
<div id="guanghu-files" class="{{Iif $showSidebar "repo-grid-filelist-sidebar" "repo-grid-filelist-only"}}">
<div class="repo-home-filelist">
{{template "repo/sub_menu" .}}
<div class="repo-button-row">
<div class="repo-button-row-left">
{{- $branchDropdownCurrentRefType := "branch" -}}
{{- $branchDropdownCurrentRefShortName := .BranchName -}}
{{- if .IsViewTag -}}
{{- $branchDropdownCurrentRefType = "tag" -}}
{{- $branchDropdownCurrentRefShortName := .TagName -}}
{{- else if .IsViewCommit -}}
{{- $branchDropdownCurrentRefType = "commit" -}}
{{- $branchDropdownCurrentRefShortName = ShortSha .CommitID -}}
{{- end -}}
{{- template "repo/branch_dropdown" dict
"Repository" .Repository
"ShowTabBranches" true
"ShowTabTags" true
"CurrentRefType" $branchDropdownCurrentRefType
"CurrentRefShortName" $branchDropdownCurrentRefShortName
"CurrentTreePath" .TreePath
"RefLinkTemplate" "{RepoLink}/src/{RefType}/{RefShortName}/{TreePath}"
"AllowCreateNewRef" .CanCreateBranch
"ShowViewAllRefsEntry" true
-}}
{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
{{$cmpBranch := ""}}
{{if ne .Repository.ID .BaseRepo.ID}}
{{$cmpBranch = printf "%s/%s:" (.Repository.OwnerName|PathEscape) (.Repository.Name|PathEscape)}}
{{end}}
{{$cmpBranch = print $cmpBranch (.BranchName|PathEscapeSegments)}}
{{$compareLink := printf "%s/compare/%s...%s" .BaseRepo.Link (.BaseRepo.DefaultBranch|PathEscapeSegments) $cmpBranch}}
<a id="new-pull-request" role="button" class="ui compact basic button" href="{{$compareLink}}"
data-tooltip-content="{{if .PullRequestCtx.Allowed}}{{ctx.Locale.Tr "repo.pulls.compare_changes"}}{{else}}{{ctx.Locale.Tr "action.compare_branch"}}{{end}}">
{{svg "octicon-git-pull-request"}}
</a>
{{end}}
{{if $isTreePathRoot}}
<a href="{{.Repository.Link}}/find/{{.BranchNameSubURL}}" class="ui compact basic button">{{ctx.Locale.Tr "repo.find_file.go_to_file"}}</a>
{{end}}
{{if and .CanWriteCode .IsViewBranch (not .Repository.IsMirror) (not .Repository.IsArchived) (not .IsViewFile)}}
<button class="ui dropdown basic compact jump button"{{if not .Repository.CanEnableEditor}} disabled{{end}}>
{{ctx.Locale.Tr "repo.editor.add_file"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
<a class="item" href="{{.RepoLink}}/_new/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">
{{ctx.Locale.Tr "repo.editor.new_file"}}
</a>
{{if .RepositoryUploadEnabled}}
<a class="item" href="{{.RepoLink}}/_upload/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">
{{ctx.Locale.Tr "repo.editor.upload_file"}}
</a>
{{end}}
<a class="item" href="{{.RepoLink}}/_diffpatch/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">
{{ctx.Locale.Tr "repo.editor.patch"}}
</a>
</div>
</button>
{{end}}
{{if and $isTreePathRoot .Repository.IsTemplate}}
<a role="button" class="ui primary compact button" href="{{AppSubUrl}}/repo/create?template_id={{.Repository.ID}}">
{{ctx.Locale.Tr "repo.use_template"}}
</a>
{{end}}
{{if not $isTreePathRoot}}
{{$treeNameIdxLast := Eval $treeNamesLen "-" 1}}
<span class="breadcrumb repo-path tw-ml-1">
<a class="section" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}" title="{{.Repository.Name}}">{{StringUtils.EllipsisString .Repository.Name 30}}</a>
{{- range $i, $v := .TreeNames -}}
<span class="breadcrumb-divider">/</span>
{{- if eq $i $treeNameIdxLast -}}
<span class="active section" title="{{$v}}">{{$v}}</span>
<button class="btn interact-fg tw-mx-1" data-clipboard-text="{{$.TreePath}}" data-tooltip-content="{{ctx.Locale.Tr "copy_path"}}">{{svg "octicon-copy" 14}}</button>
{{- else -}}
{{$p := index $.Paths $i}}<span class="section"><a href="{{$.BranchLink}}/{{PathEscapeSegments $p}}" title="{{$v}}">{{$v}}</a></span>
{{- end -}}
{{- end -}}
</span>
{{end}}
</div>
<div class="repo-button-row-right">
{{if $isTreePathRoot}}
{{template "repo/clone_panel" .}}
{{end}}
{{if and (not $isTreePathRoot) (not .IsViewFile) (not .IsBlame)}}
<a class="ui button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">
{{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}}
</a>
{{end}}
</div>
</div>
{{if .IsViewFile}}
{{template "repo/view_file" .}}
{{else if .IsBlame}}
{{template "repo/blame" .}}
{{else}}
{{if $isTreePathRoot}}
{{template "repo/code/upstream_diverging_info" .}}
{{end}}
{{template "repo/view_list" .}}
{{if and .ReadmeExist (or .IsMarkup .IsPlainText)}}
{{template "repo/view_file" .}}
{{end}}
{{end}}
</div>
{{if $showSidebar}}
{{template "repo/home_sidebar_top" .}}
{{template "repo/home_sidebar_bottom" .}}
{{end}}
</div>
</div>
</div>
{{template "base/footer" .}}