页脚加开源仓库链接

This commit is contained in:
qianyu committed 2026-09-04 19:46:58 +08:00
1 parent e467c2028d
commit c47b183e0e
16 files changed
+375 -2

No files matched your search

+2 -1
View File
@@ -46,6 +46,7 @@ type Base struct {
SiteTitle string
BaseURL string
RepoURL string
SourceURL string
AuthorGitHub string
AuthorX string
SubsitesEnabled bool
@@ -245,7 +246,7 @@ func (a *App) sameOrigin(r *http.Request) bool {
}
func (a *App) base(r *http.Request) Base {
b := Base{SiteTitle: a.cfg.SiteTitle, BaseURL: a.cfg.BaseURL, RepoURL: a.cfg.RepoURL, AuthorGitHub: a.cfg.AuthorGitHub, AuthorX: a.cfg.AuthorX, SubsitesEnabled: a.cfg.SubsitesEnabled,
b := Base{SiteTitle: a.cfg.SiteTitle, BaseURL: a.cfg.BaseURL, RepoURL: a.cfg.RepoURL, SourceURL: a.cfg.SourceURL, AuthorGitHub: a.cfg.AuthorGitHub, AuthorX: a.cfg.AuthorX, SubsitesEnabled: a.cfg.SubsitesEnabled,
IsMobile: isMobile(r), InApp: inAppBrowser(r.UserAgent()), HasBGM: hasBGM, Me: a.currentSite(r)}
if b.Me != nil {
b.MeID = b.Me.ID
+2
View File
@@ -10,6 +10,8 @@ ADMIN_PASSWORD=
# 页脚作者链接(可留空不显示)
AUTHOR_GITHUB=https://github.com/qianyubtc
AUTHOR_X=https://x.com/qianyuwing
# 本站源码仓库,页脚展示(留空不显示)
SOURCE_URL=https://github.com/qianyubtc/newbeggar
# 说明:ADMIN_PASSWORD 只在首次建站或这里的值改变时写入;主站在后台改过口令后重启不会被冲掉
# 站点标题
+2
View File
@@ -18,6 +18,7 @@ type Config struct {
AdminPassword string // 主站登录口令(路径留空 + 此口令)
SiteTitle string
RepoURL string
SourceURL string // 本站自己的开源仓库,页脚展示(留空不显示)
AuthorGitHub string // 页脚作者 GitHub 链接
AuthorX string // 页脚作者 X 链接
@@ -115,6 +116,7 @@ func loadConfig(path string) (*Config, error) {
AdminPassword: get("ADMIN_PASSWORD", get("ADMIN_TOKEN", "")),
SiteTitle: get("SITE_TITLE", "赛博要饭"),
RepoURL: get("REPO_URL", "https://github.com/qianyubtc/BinancePayTool"),
SourceURL: get("SOURCE_URL", "https://github.com/qianyubtc/newbeggar"),
AuthorGitHub: get("AUTHOR_GITHUB", "https://github.com/qianyubtc"),
AuthorX: get("AUTHOR_X", "https://x.com/qianyuwing"),
MainName: get("MAIN_NAME", "站长"),
+1
View File
@@ -3,6 +3,7 @@ package main
// 德语:key 是中文原文,占位符 %s/%d 顺序须与中文一致。查不到回落英文。
func init() {
translations["de"] = map[string]string{
"本站开源,欢迎 Star ⭐": "Diese Seite ist Open Source ⭐",
// ---- 站名 / meta ----
"赛博要饭": "Cyber-Bettler",
"赛博要饭:行行好,赏口饭吃。用币安 Pay 投币施舍,留一句话上功德簿;不花钱也能丢钢镚;用 X 发一条推就能开自己的要饭站。": "Cyber-Bettler: eine kleine Münze, gute Seele. Spende mit Binance Pay und hinterlass eine Zeile im Buch der Verdienste; pleite? wirf eine Gratis-Münze; ein Tweet auf X und dein eigener Napf steht.",
+1
View File
@@ -3,6 +3,7 @@ package main
// 英文:其它语言查不到时也回落到这里。key 是中文原文(模板/代码里的 T "…"),占位符 %s/%d 顺序须与中文一致。
func init() {
translations["en"] = map[string]string{
"本站开源,欢迎 Star ⭐": "This site is open source — star it ⭐",
// ---- 站名 / meta ----
"赛博要饭": "Cyber Beggar",
"赛博要饭:行行好,赏口饭吃。用币安 Pay 投币施舍,留一句话上功德簿;不花钱也能丢钢镚;用 X 发一条推就能开自己的要饭站。": "Cyber Beggar: spare a coin. Tip with Binance Pay and leave a line in the Book of Merit; toss a free coin if you're broke; open your own bowl with one tweet on X.",
+1
View File
@@ -3,6 +3,7 @@ package main
// 西班牙语(中性拉美 / 国际)。key 是中文原文,占位符 %s/%d 顺序须与中文一致。
func init() {
translations["es"] = map[string]string{
"本站开源,欢迎 Star ⭐": "Este sitio es de código abierto ⭐",
// ---- 站名 / meta ----
"赛博要饭": "Mendigo Cyber",
"赛博要饭:行行好,赏口饭吃。用币安 Pay 投币施舍,留一句话上功德簿;不花钱也能丢钢镚;用 X 发一条推就能开自己的要饭站。": "Mendigo Cyber: una monedita, buena gente. Dona con Binance Pay y deja una frase en el Libro de Méritos; si no tienes dinero, lanza una moneda gratis; abre tu propio cuenco con un tuit en X.",
+1
View File
@@ -3,6 +3,7 @@ package main
// 法语:key 是中文原文,占位符 %s/%d 顺序须与中文一致。查不到回落英文。
func init() {
translations["fr"] = map[string]string{
"本站开源,欢迎 Star ⭐": "Ce site est open source ⭐",
// ---- 站名 / meta ----
"赛博要饭": "Cyber Mendiant",
"赛博要饭:行行好,赏口饭吃。用币安 Pay 投币施舍,留一句话上功德簿;不花钱也能丢钢镚;用 X 发一条推就能开自己的要饭站。": "Cyber Mendiant : une petite pièce, brave âme. Donne avec Binance Pay et laisse un mot dans le Livre des Mérites ; fauché ? lance un jeton gratuit ; un tweet sur X et tu ouvres ton propre bol.",
+1
View File
@@ -3,6 +3,7 @@ package main
// 印尼语。key 是中文原文,占位符 %s/%d 顺序须与中文一致。
func init() {
translations["id"] = map[string]string{
"本站开源,欢迎 Star ⭐": "Situs ini open source ⭐",
// ---- 站名 / meta ----
"赛博要饭": "Pengemis Cyber",
"赛博要饭:行行好,赏口饭吃。用币安 Pay 投币施舍,留一句话上功德簿;不花钱也能丢钢镚;用 X 发一条推就能开自己的要饭站。": "Pengemis Cyber: kasihanilah, beri sesuap nasi. Sedekah lewat Binance Pay dan tinggalkan pesan di Buku Amal; bokek? lempar koin gratis; satu tweet di X langsung punya mangkuk sendiri.",
+1
View File
@@ -3,6 +3,7 @@ package main
// 日本語。key は中文原文(テンプレート/コードの T "…")、%s/%d の順序は中文と同じ。
func init() {
translations["ja"] = map[string]string{
"本站开源,欢迎 Star ⭐": "本サイトはオープンソースです ⭐",
// ---- 站名 / meta ----
"赛博要饭": "サイバー物乞い",
"赛博要饭:行行好,赏口饭吃。用币安 Pay 投币施舍,留一句话上功德簿;不花钱也能丢钢镚;用 X 发一条推就能开自己的要饭站。": "サイバー物乞い:お恵みを、一口だけでも。Binance Pay で投げ銭して功徳帳にひとこと残そう。お金がなくても無料コインを投げられる。X でツイート1本、自分の椀が持てる。",
+1
View File
@@ -3,6 +3,7 @@ package main
// 한국어. key 는 중국어 원문(템플릿/코드의 T "…"), %s/%d 순서는 중국어와 동일.
func init() {
translations["ko"] = map[string]string{
"本站开源,欢迎 Star ⭐": "이 사이트는 오픈소스입니다 ⭐",
// ---- 站名 / meta ----
"赛博要饭": "사이버 거지",
"赛博要饭:行行好,赏口饭吃。用币安 Pay 投币施舍,留一句话上功德簿;不花钱也能丢钢镚;用 X 发一条推就能开自己的要饭站。": "사이버 거지: 한 푼만 적선해 주세요. Binance Pay로 적선하고 공덕부에 한마디 남기기. 돈이 없으면 무료 동전이라도. X에 트윗 한 번이면 내 그릇도 열 수 있어요.",
+1
View File
@@ -3,6 +3,7 @@ package main
// 葡萄牙语(巴西)。key 是中文原文,占位符 %s/%d 顺序须与中文一致。
func init() {
translations["pt"] = map[string]string{
"本站开源,欢迎 Star ⭐": "Este site é open source ⭐",
// ---- 站名 / meta ----
"赛博要饭": "Mendigo Cyber",
"赛博要饭:行行好,赏口饭吃。用币安 Pay 投币施舍,留一句话上功德簿;不花钱也能丢钢镚;用 X 发一条推就能开自己的要饭站。": "Mendigo Cyber: uma moedinha, gente boa. Doe com Binance Pay e deixe uma frase no Livro de Méritos; sem grana, jogue uma moeda grátis; abra sua própria tigela com um tweet no X.",
+1
View File
@@ -3,6 +3,7 @@ package main
// 俄语。key 是中文原文,占位符 %s/%d 顺序须与中文一致。
func init() {
translations["ru"] = map[string]string{
"本站开源,欢迎 Star ⭐": "Сайт с открытым кодом — ставьте ⭐",
// ---- 站名 / meta ----
"赛博要饭": "Кибер-нищий",
"赛博要饭:行行好,赏口饭吃。用币安 Pay 投币施舍,留一句话上功德簿;不花钱也能丢钢镚;用 X 发一条推就能开自己的要饭站。": "Кибер-нищий: подайте монетку. Подайте через Binance Pay и оставьте строчку в Книге добрых дел; нет денег — киньте бесплатную монетку; один твит в X открывает вашу собственную миску.",
+1
View File
@@ -3,6 +3,7 @@ package main
// 土耳其语。key 是中文原文,占位符 %s/%d 顺序须与中文一致。
func init() {
translations["tr"] = map[string]string{
"本站开源,欢迎 Star ⭐": "Bu site açık kaynak ⭐",
// ---- 站名 / meta ----
"赛博要饭": "Siber Dilenci",
"赛博要饭:行行好,赏口饭吃。用币安 Pay 投币施舍,留一句话上功德簿;不花钱也能丢钢镚;用 X 发一条推就能开自己的要饭站。": "Siber Dilenci: bir sadaka ver. Binance Pay ile bağış yap ve Sevap Defteri'ne bir satır bırak; paran yoksa ücretsiz jeton at; X'te tek tweetle kendi kaseni aç.",
+1
View File
@@ -3,6 +3,7 @@ package main
// 越南语。key 是中文原文,占位符 %s/%d 顺序须与中文一致。
func init() {
translations["vi"] = map[string]string{
"本站开源,欢迎 Star ⭐": "Trang này là mã nguồn mở ⭐",
// ---- 站名 / meta ----
"赛博要饭": "Ăn Xin Cyber",
"赛博要饭:行行好,赏口饭吃。用币安 Pay 投币施舍,留一句话上功德簿;不花钱也能丢钢镚;用 X 发一条推就能开自己的要饭站。": "Ăn Xin Cyber: làm ơn làm phước, cho xin miếng cơm. Bố thí bằng Binance Pay và để lại một câu trong Sổ Công Đức; hết tiền thì ném xu miễn phí; một tweet trên X là có bát ăn xin riêng.",
+356
View File
@@ -0,0 +1,356 @@
{{define "layout"}}<!doctype html>
<html lang="{{.Lang}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<meta name="theme-color" content="#f4eedd">
<title>{{template "title" .}}</title>
<meta name="description" content="{{if .Desc}}{{.Desc}}{{else}}{{T "赛博要饭:行行好,赏口饭吃。用币安 Pay 投币施舍,留一句话上功德簿;不花钱也能丢钢镚;用 X 发一条推就能开自己的要饭站。"}}{{end}}">
<meta property="og:title" content="{{template "title" .}}">
<meta property="og:description" content="{{if .Desc}}{{.Desc}}{{else}}{{T "行行好,赏口饭吃。投币上功德簿,丢钢镚也算爱。"}}{{end}}">
<meta property="og:type" content="website">
{{if .OGImage}}<meta property="og:image" content="{{.OGImage}}">{{end}}
<meta name="twitter:card" content="summary">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ctext y='.9em' font-size='90'%3E🥣%3C/text%3E%3C/svg%3E">
{{if .NoIndex}}<meta name="robots" content="noindex">{{end}}
<link rel="preload" href="/static/pixel.woff2" as="font" type="font/woff2" crossorigin>
<style>
@font-face{font-family:"Pixel";src:url(/static/pixel.woff2) format("woff2");font-display:swap}
:root{--ink:#1e1e2e;--paper:#f4eedd;--paper-2:#e9e1ca;--panel:#fffbf0;--panel-2:#fff3cc;--gold:#ffcc00;--gold-2:#e0a800;--red:#ff4b4b;--green:#3ec46d;--blue:#3d8bfd;--purple:#8e5cff;--muted:#4a4954;--muted-2:#6f6d66;--pf:"Pixel","PingFang SC","Noto Sans SC","Microsoft YaHei",sans-serif;--line:#d9d0b8;--frame:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='6' height='6' shape-rendering='crispEdges'%3E%3Cpath fill='%231e1e2e' d='M1 0h4v1H1zM0 1h6v1H0zM0 2h2v2H0zM4 2h2v2H4zM0 4h6v1H0zM1 5h4v1H1z'/%3E%3C/svg%3E")}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;overflow-x:hidden}
html,body{max-width:100%;overscroll-behavior-x:none}
@media(max-width:899px){body{overflow-x:hidden;position:relative}}
body{margin:0;color:var(--ink);background-color:var(--paper);background-image:linear-gradient(45deg,var(--paper-2) 25%,transparent 25%,transparent 75%,var(--paper-2) 75%),linear-gradient(45deg,var(--paper-2) 25%,transparent 25%,transparent 75%,var(--paper-2) 75%);background-size:16px 16px;background-position:0 0,8px 8px;font:15px/1.7 -apple-system,BlinkMacSystemFont,"PingFang SC","Noto Sans SC","Hiragino Sans GB","Microsoft YaHei","Helvetica Neue",Arial,sans-serif;min-height:100vh}
.pf,h1,h2,.brand,.ticker,.lv,.pname,.stat .v,.stat .k,.amt-btn,.btn,.tab,.amtv,.rank,.insert,.barlabel,.tag,.nav a,.name,.av,.section-title,.copy,.anchors a,.step::before,.big,.dialog,.me,.count,.card h2 .sub,.kv .k,.empty .ico{font-family:var(--pf)}
.pf12{font-size:12px}
a{color:var(--blue)}
.num{font-variant-numeric:tabular-nums}
.accent{color:var(--gold-2)}
.page{max-width:1080px;margin:0 auto;padding:0 16px 96px}
@media(min-width:900px){.page{padding-bottom:40px}}
.px{border:4px solid transparent;border-image:var(--frame) 2 / 4px stretch;image-rendering:pixelated;background-clip:padding-box}
.nav{display:flex;align-items:center;gap:6px;height:64px}
.langsel{display:inline-flex;align-items:center;flex:none;position:relative;cursor:pointer}.langsel select{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer;font-size:16px}
.langcur{display:inline-flex;align-items:center;gap:2px;font-family:var(--pf);font-size:14px;line-height:1;color:var(--ink);background:#fff;border:2px solid var(--ink);box-shadow:2px 2px 0 var(--ink);padding:5px 6px;min-width:34px;justify-content:center}
.langcur i{font-style:normal;font-size:9px;opacity:.6}
.sfx{background:none;border:0;line-height:0;padding:8px 4px;cursor:pointer;min-width:34px;min-height:36px;display:inline-flex;align-items:center;justify-content:center;flex:none}
.px-spk .spk-x{display:none}.sfx.off .spk-w1,.sfx.off .spk-w2{display:none}.sfx.off .spk-x{display:inline}
.sfx.idle .spk-w2{opacity:.25}
.navlogin{display:inline-flex;align-items:center;gap:5px}.px-key{flex:none}
.navrank{display:inline-flex;align-items:center;gap:5px}.px-trophy{flex:none}
.sfx.idle{animation:sfxnudge 1.2s ease-in-out infinite}@keyframes sfxnudge{0%,100%{transform:scale(1)}50%{transform:scale(1.18)}}@media(prefers-reduced-motion:reduce){.sfx.idle{animation:none}}
.nav>*{flex:none}.nav .brand{flex:0 1 auto;min-width:0}.nav .brand span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media(max-width:430px){.nav{gap:4px}.nav .brand{font-size:20px;margin-right:2px}.nav .brand .px-coin{display:none}.sfx{min-width:28px;padding:6px 2px;font-size:16px}.nav a.link{padding:8px 5px;font-size:15px}.nav a.pill{padding:6px 10px;font-size:15px}
.nav a.navrank span{display:none}.nav .navlogin .lbl{display:none}}
.brand{display:flex;align-items:center;gap:8px;font-size:24px;line-height:1;color:var(--ink);text-decoration:none;white-space:nowrap;margin-right:6px}
.brand .px-coin{width:28px;height:28px;flex:none}
.nav .sp{flex:1}
.nav a.link{color:var(--ink);text-decoration:none;font-size:16px;padding:8px 8px;white-space:nowrap}
.nav a.link:hover{background:var(--panel-2)}
.nav a.pill{color:var(--ink);background:var(--gold);font-size:16px;padding:6px 12px;text-decoration:none;white-space:nowrap;box-shadow:3px 3px 0 var(--ink)}
.nav a.pill:active{transform:translate(2px,2px);box-shadow:1px 1px 0 var(--ink)}
@media(max-width:420px){.hide-sm{display:none}.nav a.pill{font-size:12px;padding:6px 10px}.nav a.link{font-size:12px;padding:8px 5px}}
.ticker{overflow:hidden;contain:paint;touch-action:pan-y;background:var(--ink);color:var(--gold);font-size:12px;line-height:1.5;padding:6px 0;margin:0 0 16px;white-space:nowrap;box-shadow:3px 3px 0 rgba(30,30,46,.25)}
.ticker-in{display:inline-block;animation:tick 34s linear infinite}
.ticker-in span{padding:0 10px}
@keyframes tick{to{transform:translateX(-50%)}}
@media(prefers-reduced-motion:reduce){.ticker-in,.sprite .bowl,.sprite .eyes,.sprite .tear,.blink,.jump .sprite,.dialog .cursor,.flycoin{animation:none!important}}
.layout{display:grid;gap:18px}
@media(min-width:900px){.layout{grid-template-columns:400px minmax(0,1fr);gap:24px;align-items:start}.col-l{position:sticky;top:16px}.layout.cols2{grid-template-columns:1fr 1fr}}
.col-l,.col-r{display:grid;gap:18px;min-width:0}
.narrow{max-width:560px;margin:0 auto}
.card{background:var(--panel);padding:18px;box-shadow:4px 4px 0 var(--ink)}
.card h2{font-size:18px;margin:0 0 12px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;line-height:1.3}
.card h2 .sub{color:var(--muted);font-size:12px}
h1{font-size:24px;margin:0 0 8px;line-height:1.3}
/* 角色卡 */
.char-top{display:flex;gap:16px;align-items:flex-start}
.sprite-wrap{flex:none;width:96px;display:grid;place-items:end center}
.sprite{width:96px;height:auto;image-rendering:pixelated}
@media(min-width:900px){.char-top .sprite-wrap,.char-top .sprite{width:120px}}
.sprite .bowl{animation:shake 1s steps(1) infinite}
@keyframes shake{0%,100%{transform:translateY(0)}50%{transform:translateY(-1px)}}
.sprite .eyes{animation:blink 4s steps(1) infinite}
@keyframes blink{0%,90%{opacity:1}91%,96%{opacity:0}97%,100%{opacity:1}}
.sprite .tear{animation:tear 2.4s steps(1) infinite}
@keyframes tear{0%{transform:translateY(0)}33%{transform:translateY(1px)}66%{transform:translateY(2px)}}
.jump .sprite{animation:jump .9s steps(1) infinite}
@keyframes jump{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
.char-info{flex:1;min-width:0}
.lvrow{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}
.lv{display:inline-block;background:var(--ink);color:var(--gold);font-size:12px;line-height:1.6;padding:2px 8px}
.lv b{color:#fff;font-weight:normal;margin-left:6px}
.lv.gold{background:var(--gold);color:var(--ink);border:2px solid var(--ink);padding:0 8px}
.pname{font-size:24px;margin:0 0 8px;line-height:1.25;word-break:break-word}
.barlabel{display:flex;justify-content:space-between;align-items:baseline;font-size:12px;color:var(--muted);margin-top:8px;line-height:1.4}
.barlabel b{color:var(--ink);font-weight:normal;margin-left:4px}
.barlabel span{font-size:12px;color:var(--muted);margin-left:8px;text-align:right}
.bar{position:relative;height:14px;border:3px solid var(--ink);background:#fff;margin:2px 0 0;box-shadow:2px 2px 0 var(--ink)}
.bar span{display:block;height:100%;background:var(--green);background-image:repeating-linear-gradient(90deg,transparent 0 6px,rgba(0,0,0,.14) 6px 8px);transition:width .6s steps(12)}
.bar.hp span{background-color:var(--red)}
.xpill{display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:700;padding:3px 8px;border:3px solid var(--ink);background:#fff;color:var(--ink);text-decoration:none;box-shadow:2px 2px 0 var(--ink);margin-top:6px}
.dialog{position:relative;margin-top:16px;background:#fff;padding:10px 26px 10px 12px;font-size:16px;line-height:1.6;box-shadow:3px 3px 0 var(--ink);min-height:44px;word-break:break-word}
.dialog::before{content:"▶";position:absolute;left:-2px;top:-14px;background:var(--gold);padding:0 6px;font-size:10px;border:3px solid var(--ink)}
.dialog .cursor{position:absolute;right:8px;bottom:6px;font-size:10px;animation:blinkc .8s steps(1) infinite}
@keyframes blinkc{50%{opacity:0}}
.more{background:none;border:0;color:var(--blue);font-size:12px;cursor:pointer;padding:6px 0 0;font-family:inherit}
/* HUD */
.stats{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.stat{background:var(--panel);padding:10px 12px;min-width:0;box-shadow:3px 3px 0 var(--ink)}
.stat.main{grid-column:1/-1;background:var(--gold);display:flex;align-items:center;gap:12px}
.stat .k{font-size:12px;color:var(--muted);line-height:1.6}
.stat.main .k{color:var(--ink)}
.stat .v{font-size:24px;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.2}
.stat.main .v{font-size:36px;line-height:1.1}
.stat .v small{font-size:12px;color:var(--muted);margin-left:5px}
.stat.main .v small{color:var(--ink)}
.stat.main .bowlv{display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 14px;white-space:nowrap;overflow:visible}
.stat.main .bowlv>span:first-child{min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis}
.stat.main .bowlcoins{font-size:22px;display:inline-flex;align-items:center;gap:5px}
.stat.main .bowlcoins .px-coin{width:18px;height:18px;flex:none}
.stat.main .bowlcoins small{margin-left:0}
.stat .px-bowl{width:48px;height:auto;flex:none}
.stat.main .sub2{display:flex;flex-direction:column;gap:8px;align-self:center;flex:none;font-family:var(--pf);color:var(--ink);text-align:left}
.stat.main .sub2 span{display:flex;align-items:baseline;justify-content:flex-start;gap:5px;white-space:nowrap;background:#fff;border:2px solid var(--ink);box-shadow:2px 2px 0 var(--ink);padding:3px 9px;min-width:96px}
.stat.main .sub2 i{font-style:normal;font-size:11px;opacity:.7}.stat.main .sub2 b{font-size:16px;font-weight:400}.stat.main .sub2 small{font-size:11px;opacity:.7}
@media(max-width:430px){.stat.main .v{font-size:30px}.stat.main .bowlcoins{font-size:18px}.stat.main{gap:8px}.stat .px-bowl{width:40px}}
/* 表单 */
label{display:block;font-size:13px;color:var(--ink);font-weight:600;margin:14px 0 6px}
form>label:first-child,.grid2>div>label:first-child{margin-top:0}
input,textarea,select{width:100%;padding:10px 12px;border:3px solid var(--ink);background:#fff;color:var(--ink);font-size:16px;font-family:inherit;line-height:1.4;border-radius:0;box-shadow:inset 3px 3px 0 var(--paper-2)}
input::placeholder,textarea::placeholder{color:#8f8a7c}
input:focus,textarea:focus,select:focus{outline:none;border-color:var(--blue);box-shadow:3px 3px 0 var(--blue)}
textarea{resize:vertical;min-height:70px}
.hint{font-size:13px;color:var(--muted);margin-top:6px;line-height:1.6}
.err{color:var(--red);font-size:13px;margin-top:10px}
.ok{color:var(--green);font-size:13px}
.insert{text-align:center;font-size:12px;letter-spacing:.1em;color:var(--red);margin:-4px 0 12px;line-height:1.6}
.blink{animation:blinkc 1s steps(1) infinite}
.amounts{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
.amt-btn{display:flex;align-items:center;justify-content:center;gap:5px;padding:10px 4px;border:3px solid var(--ink);background:#fff;color:var(--ink);font-size:16px;white-space:nowrap;cursor:pointer;font-family:inherit;min-height:46px;box-shadow:3px 3px 0 var(--ink)}
.amt-btn .px-coin{width:16px;height:16px}
.amt-btn.on{background:var(--gold);transform:translate(2px,2px);box-shadow:1px 1px 0 var(--ink)}
.count{float:right;font-size:12px;color:var(--muted-2)}
.btn{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;min-height:50px;border:4px solid transparent;border-image:var(--frame) 2 / 4px stretch;image-rendering:pixelated;background-clip:padding-box;padding:10px 16px;font-size:18px;cursor:pointer;background-color:var(--gold);color:var(--ink);text-decoration:none;box-shadow:4px 4px 0 var(--ink)}
.btn:active{transform:translate(3px,3px);box-shadow:1px 1px 0 var(--ink)}
.btn.sec{background-color:#fff}
.btn.ghost{background-color:transparent;box-shadow:none;color:var(--muted)}
.btn.danger{background-color:#fff;color:var(--red)}
.btn.sm{display:inline-flex;width:auto;min-height:40px;padding:6px 12px;font-size:14px;box-shadow:3px 3px 0 var(--ink)}
.btns{display:flex;gap:12px;flex-wrap:wrap;margin-top:14px}
.btns .btn{flex:1 1 auto;white-space:nowrap}
/* Tab */
.tabs{display:grid;grid-template-columns:repeat(3,1fr);border:3px solid var(--ink);margin-bottom:14px;background:#fff;box-shadow:3px 3px 0 var(--ink)}
.tab{border:0;border-right:3px solid var(--ink);background:transparent;color:var(--muted);font-size:16px;padding:10px 4px;cursor:pointer;white-space:nowrap}
.tab:last-child{border-right:0}
.tab.on{background:var(--gold);color:var(--ink)}
.panel{display:none}.panel.on{display:block}
/* 榜单 / 功德簿 */
.list{list-style:none;margin:0;padding:0}
.item{display:flex;align-items:center;gap:10px;padding:10px 0;border-top:3px dotted var(--line)}
.item:first-child{border-top:0;padding-top:2px}
.av{width:36px;height:36px;flex:none;display:grid;place-items:center;font-size:16px;background:hsl(var(--h) 75% 86%);color:hsl(var(--h) 55% 28%);border:3px solid var(--ink);box-shadow:2px 2px 0 var(--ink)}
.av.sq{background:#fff;font-size:20px;color:var(--ink)}
.rank{width:30px;flex:none;text-align:center;font-size:16px;color:var(--muted)}
.rank.m{font-size:20px}
.body{flex:1;min-width:0}
.name{display:flex;align-items:center;gap:6px;min-width:0;font-size:16px}
.name>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.name a{color:inherit;text-decoration:none}
.sub{font-size:13px;color:var(--muted);margin-top:1px}
.amtv .px-coin{width:14px;height:14px;vertical-align:-2px}
.amtv{white-space:nowrap;font-size:18px}
.feed .item{flex-wrap:wrap}
.bubble{flex-basis:100%;margin:8px 0 0 46px;background:#fff;border:3px solid var(--ink);padding:6px 10px;font-size:14px;line-height:1.6;word-break:break-word;position:relative;box-shadow:2px 2px 0 var(--ink)}
.bubble::before{content:"";position:absolute;left:-9px;top:8px;border:3px solid transparent;border-right-color:var(--ink);border-width:4px 6px 4px 0}
.bubble.blocked{color:var(--muted-2);letter-spacing:2px}
.time{font-size:12px;color:var(--muted);white-space:nowrap}
.me{font-size:10px;color:var(--ink);background:var(--gold);border:2px solid var(--ink);padding:0 5px;flex:none}
.empty{text-align:center;color:var(--muted);padding:24px 12px;font-size:14px}
.fold>summary{list-style:none;cursor:pointer;-webkit-tap-highlight-color:transparent}.fold>summary::-webkit-details-marker{display:none}
.fold>summary h2{margin:0;display:flex;align-items:center;gap:8px}.fold>summary .caret{margin-left:auto;font-size:18px;transition:transform .15s}.fold[open]>summary .caret{transform:rotate(90deg)}
.fold[open]>summary h2{margin-bottom:12px}
.pager{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:12px;font-family:var(--pf);font-size:14px}
.pager a,.pager span.cur,.pager span.dis{display:inline-flex;align-items:center;justify-content:center;min-width:36px;min-height:36px;padding:0 10px;border:3px solid var(--ink);background:#fff;color:var(--ink);text-decoration:none;box-shadow:2px 2px 0 var(--ink)}
.pager span.cur{background:var(--gold)}.pager span.dis{opacity:.4;box-shadow:none}
.empty .ico{font-size:32px;margin-bottom:6px}
.empty .sprite{width:72px}
.cta{text-align:center}
.cta p{color:var(--ink);margin:0 0 12px;font-size:14px}
.fab{position:fixed;left:16px;right:16px;bottom:calc(16px + env(safe-area-inset-bottom));z-index:20;display:none}
.fab.show{display:block}
@media(min-width:900px){.fab{display:none!important}.only-mobile{display:none!important}}
@media(max-width:899px){.only-desktop{display:none!important}}
.notice{background:var(--panel-2);border:3px solid var(--ink);padding:10px 12px;font-size:14px;line-height:1.6;box-shadow:3px 3px 0 var(--ink)}
.notice.ok{background:#dff7e8}
.notice.bad{background:#ffe1e1}
.code{background:#fff;border:2px solid var(--ink);padding:1px 6px;font-size:13px;word-break:break-all}
.copy{cursor:pointer;border:3px solid var(--ink);background:#fff;color:var(--ink);padding:6px 10px;min-height:36px;font-size:12px;flex:none;box-shadow:2px 2px 0 var(--ink)}
.kv{display:grid;grid-template-columns:auto 1fr;gap:10px 14px;font-size:14px;align-items:center}
.kv .k{color:var(--muted)}
.kv .v{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.qr{display:block;margin:8px auto;background:#fff;padding:8px;width:240px;height:240px;border:3px solid var(--ink);box-shadow:3px 3px 0 var(--ink);image-rendering:pixelated}
.steps{counter-reset:s;display:grid;gap:18px}
.step{position:relative;padding-left:44px}
.step::before{counter-increment:s;content:counter(s);position:absolute;left:0;top:-2px;width:30px;height:30px;background:var(--gold);color:var(--ink);border:3px solid var(--ink);font-size:14px;display:grid;place-items:center;box-shadow:2px 2px 0 var(--ink)}
.step h2{margin-bottom:8px}
.big{font-size:36px;line-height:1.2}
.big small{font-size:12px;color:var(--muted);margin-left:6px}
.spin{width:44px;height:44px;border:4px solid var(--line);border-top-color:var(--ink);margin:12px auto 16px;animation:spin 1s steps(8) infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.center{text-align:center}
.tag{display:inline-block;padding:0 6px;font-size:12px;line-height:1.6;border:2px solid var(--ink);color:var(--ink);white-space:nowrap;background:#fff}
.tag.paid{background:#dff7e8}.tag.claimed{background:var(--panel-2)}.tag.bad{background:#ffe1e1;color:var(--red);border-color:var(--red)}
.grid2{display:grid;gap:0 14px}@media(min-width:600px){.grid2{grid-template-columns:1fr 1fr}}
.emojis{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.emojis button{font-size:20px;background:#fff;border:3px solid var(--ink);padding:3px 7px;cursor:pointer;line-height:1.3;box-shadow:2px 2px 0 var(--ink)}
.anchors{display:flex;gap:10px;overflow-x:auto;padding:2px 2px 6px;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.anchors::-webkit-scrollbar{display:none}
.anchors a{flex:none;font-size:12px;line-height:1.8;color:var(--ink);text-decoration:none;border:3px solid var(--ink);padding:4px 10px;background:#fff;box-shadow:2px 2px 0 var(--ink)}
.actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:10px;flex-basis:100%;padding-left:46px}
.actions form,.inline form{display:inline}
.section-title{font-size:12px;color:var(--muted);letter-spacing:.1em;margin:22px 0 8px;line-height:1.6}
.xavatar{width:40px;height:40px;border:3px solid var(--ink);vertical-align:middle;margin-right:6px;image-rendering:auto;object-fit:cover;background:#fff}
.av.img{object-fit:cover;background:#fff}
.xh{font-size:12px;color:var(--muted);font-family:var(--pf);flex:none}
.badge{font-size:14px;flex:none;letter-spacing:-2px}
.coinrow{display:flex;align-items:center;gap:10px;margin-top:14px;flex-wrap:wrap}
.js .col-l>.donate-card{display:none}
#modalForm .donate-card{box-shadow:none;border:0;padding:0;background:transparent}
#modalForm .donate-card h2{padding-right:44px}
.coinrow .coins b{color:var(--ink);font-size:16px}
.coinbtn .px-coin{width:16px;height:16px}
.coinbtn:disabled{opacity:.6;cursor:default}
.flycoin{position:fixed;z-index:50;font-size:28px;animation:fly .9s ease-out forwards;pointer-events:none}
@keyframes fly{0%{transform:translate(-50%,0) scale(1);opacity:1}60%{transform:translate(-50%,-90px) scale(1.3);opacity:1}100%{transform:translate(-50%,-140px) scale(.6);opacity:0}}
.hdr{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.hdr .body{flex:1 1 220px;min-width:0}
.hdr-actions{display:flex;gap:8px;margin-left:auto;flex:none}.hdr-actions form{display:contents}
@media(max-width:599px){.hdr-actions{flex-basis:100%;margin-left:0}.hdr-actions .btn{flex:1}}
.toast{position:fixed;left:50%;bottom:calc(84px + env(safe-area-inset-bottom));transform:translateX(-50%);background:var(--ink);color:var(--gold);padding:8px 14px;font-family:var(--pf);font-size:14px;z-index:60;box-shadow:3px 3px 0 rgba(0,0,0,.25)}
.tabs.four{grid-template-columns:repeat(4,1fr)}
.tabs.four .tab{font-size:14px;padding:10px 2px}
.tab.on::before{content:"▶";font-size:8px;margin-right:3px}
.toggle{display:inline-flex;border:3px solid var(--ink);margin-bottom:10px;background:#fff}
.toggle button{border:0;background:transparent;font-family:var(--pf);font-size:12px;padding:8px 12px;min-height:36px;cursor:pointer;color:var(--muted)}
.toggle button.on{background:var(--ink);color:var(--gold)}
.bubble.reply{background:var(--panel-2);margin-top:6px}
.profrow{display:flex;align-items:center;gap:12px;margin:12px 0}
.profrow .xavatar{width:56px;height:56px;margin:0}
.codebox{display:flex;align-items:center;gap:10px;margin:8px 0 12px}
.codebox .code.big{font-size:24px;padding:6px 12px;font-family:var(--pf)}
.sharebox{margin-top:14px}
.sharebox img{display:block;max-width:100%;border:3px solid var(--ink);box-shadow:4px 4px 0 var(--ink)}
.modal{position:fixed;inset:0;background:rgba(30,30,46,.6);z-index:100;display:flex;align-items:flex-end;justify-content:center}
.modal[hidden]{display:none}
.modal-box{position:relative;width:100%;max-height:92vh;overflow:auto;background:var(--panel);padding:18px 16px calc(18px + env(safe-area-inset-bottom));border-top:4px solid var(--ink);box-shadow:0 -6px 0 var(--ink)}
@media(min-width:600px){.modal{align-items:center;padding:20px}.modal-box{width:520px;max-height:90vh;border:4px solid var(--ink);box-shadow:6px 6px 0 var(--ink)}}
.modal-x{position:absolute;right:10px;top:8px;width:36px;height:36px;border:3px solid var(--ink);background:#fff;font-family:var(--pf);font-size:16px;cursor:pointer;box-shadow:2px 2px 0 var(--ink);z-index:1}
.spin-inline{display:inline-block;width:14px;height:14px;border:3px solid var(--line);border-top-color:var(--ink);border-radius:50%;animation:spin 1s steps(8) infinite;vertical-align:-2px;margin-right:4px}
.paypanel .qr{width:200px;height:200px}
.paypanel .big{font-size:32px}
footer{color:var(--muted);font-size:13px;text-align:center;margin-top:32px;line-height:1.9}
footer a{color:var(--muted)}
</style>
</head>
<body>
{{if .HasBGM}}<audio id="bgm" src="/static/bgm.m4a" loop preload="none" playsinline></audio>{{end}}
<div class="page">
<nav class="nav">
<a class="brand" href="{{.HomePath}}"{{if ne .HomePath "/"}} title="{{T "回到刚才看的碗"}}"{{end}}>{{template "coin"}}<span>{{T .SiteTitle}}</span></a>
<span class="sp"></span>
<label class="langsel" title="Language / 语言"><span class="langcur" aria-hidden="true">{{.LangShort}}<i>▾</i></span><select onchange="location.href='/lang?to='+encodeURIComponent(this.value)" aria-label="Language">{{range .Langs}}<option value="{{.Code}}"{{if eq .Code $.Lang}} selected{{end}}>{{.Name}}</option>{{end}}</select></label>
<button class="sfx" id="sfxBtn" type="button" aria-label="{{T "声音开关"}}" title="{{T "声音"}}">{{template "spk"}}</button>
<a class="link navrank" href="/rank" aria-label="{{T "排行榜"}}" title="{{T "排行榜"}}">{{template "trophy"}}<span>{{T "排行榜"}}</span></a>
{{if .Me}}<a class="link" href="/manage">{{T "我的后台"}}</a>{{else}}<a class="link navlogin" href="/login">{{template "keyicon"}}<span class="hide-sm">{{T "站长"}}</span><span class="lbl">{{T "登录"}}</span></a>{{end}}
{{if .Me}}<a class="pill px" href="/{{.Me.Slug}}">{{T "我的站"}}</a>{{else if .SubsitesEnabled}}<a class="pill px" href="/new">{{T "开通要饭站"}}</a>{{end}}
</nav>
<div class="ticker" aria-hidden="true"><div class="ticker-in"><span>{{T "🙏 行行好,赏口饭吃"}}</span><span>{{T "🪙 一毛也是爱"}}</span><span>{{T "💸 投币就上榜"}}</span><span>{{T "📜 留一句话刻功德簿"}}</span><span>{{T "🏚 发条推就能开自己的要饭站"}}</span><span>{{T "👾 赛博丐帮,人人有碗"}}</span><span>{{T "🙏 行行好,赏口饭吃"}}</span><span>{{T "🪙 一毛也是爱"}}</span><span>{{T "💸 投币就上榜"}}</span><span>{{T "📜 留一句话刻功德簿"}}</span><span>{{T "🏚 发条推就能开自己的要饭站"}}</span><span>{{T "👾 赛博丐帮,人人有碗"}}</span></div></div>
{{template "content" .}}
<footer>
{{T .SiteTitle}} {{T "· 赛博丐帮,行行好"}}<br>
{{if or .AuthorX .AuthorGitHub}}{{T "作者"}}{{if .AuthorX}} <a href="{{.AuthorX}}" target="_blank" rel="noopener">𝕏 @{{lastSeg .AuthorX}}</a>{{end}}{{if .AuthorGitHub}} · <a href="{{.AuthorGitHub}}" target="_blank" rel="noopener">GitHub {{lastSeg .AuthorGitHub}}</a>{{end}}<br>{{end}}
{{T "收款由"}} <a href="{{.RepoURL}}" target="_blank" rel="noopener">BinancePayTool</a> {{T "驱动 · 字体"}} <a href="https://github.com/TakWolf/fusion-pixel-font" target="_blank" rel="noopener">{{T "缝合怪像素字体"}}</a>{{if .SourceURL}}<br>
<a href="{{.SourceURL}}" target="_blank" rel="noopener">{{T "本站开源,欢迎 Star ⭐"}}</a>{{end}}
</footer>
</div>
<script>
// 声音:背景音乐(/static/bgm.m4a 循环)+ 8-bit 音效(Web Audio 现场合成,不加载文件)。一个开关管全部,记 localStorage(sfx),默认开。
// 浏览器不让页面一打开就出声:先尝试自动播放,被拦就等用户第一次触屏/点击/按键再起。音效首次出声也必须在用户手势里创建 AudioContext(iOS 限制)。
// 背景音乐也解码进同一个 AudioContext 里循环播放(AudioBufferSourceNode,无缝循环):和 <audio> 元素分开走的话,iOS 上音效一起音频会话就会把音乐打断重来。
// 解码不了(极少数浏览器没有 AAC 解码器)才退回 <audio> 元素。
var BGM_VOL=0.45;
window.sfx=(function(){var ctx,on=true,C=window.AudioContext||window.webkitAudioContext,buf=null,src=null,el=null,bytes=null,elMode=false,loading=false,unlocked=false;try{on=localStorage.getItem('sfx')!=='0'}catch(e){}
function ac(){if(!C)return null;if(!ctx){try{ctx=new C()}catch(e){return null}}if(ctx.state==='suspended'){try{ctx.resume()}catch(e){}}return ctx}
// 在用户手势里解锁:resume + 放一个空 buffer(老 iOS 的解锁套路)
function unlock(){var c=ac();if(!c)return;try{var b=c.createBuffer(1,1,22050),s=c.createBufferSource();s.buffer=b;s.connect(c.destination);s.start(0);unlocked=true}catch(e){}}
var paint=function(){};
// 下载一次:字节既喂给 <audio>(手势里能立刻响)也喂给 decodeAudioData(解码完切到 Web Audio 无缝循环)
function loadBgm(){if(bytes||loading)return;el=el||document.getElementById('bgm');if(!el)return;loading=true;
fetch(el.getAttribute('src')).then(function(r){if(!r.ok)throw 0;return r.arrayBuffer()}).then(function(ab){bytes=ab;
try{el.src=URL.createObjectURL(new Blob([ab],{type:'audio/mp4'}))}catch(e){}
var c=C&&ac();if(!c){elMode=true;loading=false;startBgm();return}
return new Promise(function(res,rej){var p=c.decodeAudioData(ab.slice(0),res,rej);if(p&&p.then)p.then(res,rej)}).then(function(b){buf=b;loading=false;startBgm()})})
.catch(function(){loading=false;elMode=true;startBgm()})}
function elPlay(){if(!el||!el.paused)return;el.volume=BGM_VOL;var p=el.play();if(p&&p.then)p.then(paint,function(){})}
function startBgm(){if(!on)return;
if(elMode||!buf){ // 还没解码好(或解码不了):先用 <audio> 顶上,只有在手势里才播得出来
if(bytes)elPlay();return}
var c=ac();if(!c)return;
if(c.state!=='running'){var r=c.resume&&c.resume();if(r&&r.then)r.then(function(){if(c.state==='running'&&!src)startBgm();paint()},function(){});return}
if(src)return;
var off=0;if(el&&!el.paused){off=el.currentTime%buf.duration;el.pause()} // 从 <audio> 接力,不从头来
var s=c.createBufferSource(),g=c.createGain();s.buffer=buf;s.loop=true;g.gain.value=BGM_VOL;s.connect(g);g.connect(c.destination);s.start(0,off);src=s;paint()}
function stopBgm(){if(src){try{src.stop()}catch(e){}src=null}if(el)el.pause();paint()}
function playingNow(){if(src)return ctx&&ctx.state==='running';return !!(el&&!el.paused)}
function armBgm(){['pointerdown','touchend','click','keydown'].forEach(function(t){document.addEventListener(t,function(){if(!on)return;unlock();loadBgm();startBgm()},{passive:true})})}
function tone(f,t0,dur,type,vol){var c=ac();if(!c)return;var o=c.createOscillator(),g=c.createGain(),t=c.currentTime+t0;o.type=type||'square';o.frequency.setValueAtTime(f,t);
g.gain.setValueAtTime(0.0001,t);g.gain.exponentialRampToValueAtTime(vol||0.22,t+0.012);g.gain.exponentialRampToValueAtTime(0.0001,t+dur);o.connect(g);g.connect(c.destination);o.start(t);o.stop(t+dur+0.03)}
function play(seq){if(!on)return;try{seq.forEach(function(n){tone(n[0],n[1],n[2],n[3],n[4])})}catch(e){}}
var S={
warm:function(){if(on)ac()},
click:function(){play([[740,0,0.045,'square',0.12]])},
coin:function(){play([[988,0,0.07,'square',0.25],[1319,0.07,0.32,'square',0.25]])},
ok:function(){play([[523,0,0.09],[659,0.09,0.09],[784,0.18,0.09],[1047,0.27,0.38,'square',0.26]])},
fail:function(){play([[196,0,0.1,'sawtooth',0.16],[147,0.1,0.26,'sawtooth',0.16]])},
levelup:function(){play([[659,0,0.08],[784,0.08,0.08],[988,0.16,0.08],[1319,0.24,0.08],[1568,0.32,0.4,'square',0.26]])},
enabled:function(){return on},
playing:function(){return src?'webaudio':(el&&!el.paused?'element':'')},
toggle:function(){on=!on;try{localStorage.setItem('sfx',on?'1':'0')}catch(e){}if(on){S.coin();loadBgm();startBgm()}else{stopBgm()}paint();return on}};
document.addEventListener('DOMContentLoaded',function(){if(on)loadBgm();armBgm();
var b=document.getElementById('sfxBtn');if(!b)return;
// 三态:🔊 在放 / 🔈 开着但还没放(浏览器拦了自动播放,点一下就放)/ 🔇 静音
var hasBgm=!!document.getElementById('bgm'); // 没放音乐文件时只有音效,喇叭只有开/关两态
paint=function(){var p=on&&(!hasBgm||playingNow());b.classList.toggle('off',!on);b.classList.toggle('idle',on&&!p);b.title=!on?{{T "声音:关"}}:(p?{{T "声音:开(点击静音)"}}:{{T "点一下开始播放"}})};paint();
b.addEventListener('click',function(){if(on&&!playingNow()){loadBgm();startBgm();return}S.toggle()})});
document.addEventListener('click',function(ev){var b=ev.target.closest('.btn,.tab,.amt-btn,.copy,.pill,.toggle button,.link');if(!b||b.disabled)return;ac();if(b.id!=='coinbtn')S.click()},true);
return S})();
(function(){
// 复制按钮(全站委托)
document.addEventListener('click',function(ev){var b=ev.target.closest('.copy');if(!b||b.id==='nextText')return;var src=b.dataset.copyFrom;var t=src?document.getElementById(src).value:b.dataset.copy;var o=b.textContent;
function done(){b.textContent={{T "已复制"}};setTimeout(function(){b.textContent=o},1500)}
(navigator.clipboard?navigator.clipboard.writeText(t):Promise.reject()).then(done,function(){var i=document.createElement('input');i.value=t;document.body.appendChild(i);i.select();try{document.execCommand('copy')}catch(e){}i.remove();done()})});
// 付款面板:轮询到账、回填/自报、成功态
window.initPayPanel=function(pp,onDone){
var code=pp.dataset.code,site=pp.dataset.site,amount=pp.dataset.amount,status=pp.dataset.status,stop=false,fails=0;
function success(amt){stop=true;if(window.sfx)sfx.ok();var sp=document.querySelector('.sprite');pp.innerHTML='<div class="center"><div class="sprite-wrap jump" style="margin:0 auto 8px">'+(sp?sp.outerHTML:'🥣')+'</div><div class="insert">★ THANK YOU ★</div><h2 class="center" style="justify-content:center">🙏 '+{{T "功德 +"}}+(amt||amount)+'</h2><p class="hint">'+{{T "你的名号和留言已刻上功德簿"}}+'</p><div class="btns"><a class="btn" href="'+site+'#feed" onclick="if(location.pathname===this.pathname){location.hash=\'feed\';location.reload();return false}">'+{{T "看看功德簿"}}+'</a><a class="btn sec" href="/d/'+code+'">'+{{T "分享卡片"}}+'</a></div></div>';if(onDone)onDone('paid')}
function setStatus(html){var st=pp.querySelector('#ppStatus');if(st)st.innerHTML=html}
function apply(j){if(!j||!j.status)return;if(j.status==='paid'){success(j.amount)}else if(j.status==='claimed'&&status!=='claimed'){status='claimed';setStatus('<div class="notice" style="margin-top:12px"><span class="spin-inline"></span> '+{{T "已提交,等站长确认后上榜。本页会自动更新。"}}+'</div>')}else if(['expired','failed','rejected','closed'].indexOf(j.status)>=0){stop=true;setStatus('<div class="notice bad" style="margin-top:12px">'+{{T "这笔施舍已"}}+({expired:{{T "过期"}},failed:{{T "下单失败"}},rejected:{{T "被站长拒绝"}},closed:{{T "关闭"}}})[j.status]+{{T ",请回到页面重新投币。"}}+'</div>')}}
function tick(){if(stop)return;fetch('/d/'+code+'/status',{cache:'no-store'}).then(function(r){return r.json()}).then(function(j){fails=0;apply(j)}).catch(function(){if(++fails>=20)stop=true})}
var timer=setInterval(function(){if(stop){clearInterval(timer);return}tick()},3000);
pp.addEventListener('submit',function(ev){var f=ev.target.closest('.claimf');if(!f)return;ev.preventDefault();var fd=new FormData(f);fd.append('ajax','1');var btn=f.querySelector('button');btn.disabled=true;var err=f.querySelector('.claim-err');err.textContent='';
fetch(f.getAttribute('action'),{method:'POST',body:fd}).then(function(r){return r.json()}).then(function(j){btn.disabled=false;if(j.status==='paid'){success()}else if(j.ok&&j.status==='claimed'){apply({status:'claimed'})}else{if(window.sfx)sfx.fail();err.textContent=j.msg||{{T "没成功,再试试"}}}}).catch(function(){btn.disabled=false;if(window.sfx)sfx.fail();err.textContent={{T "网络开小差了"}}})});
return function(){stop=true;clearInterval(timer)}
};
var pp=document.getElementById('paypanel');if(pp)window.initPayPanel(pp);
})();
</script>
{{block "scripts" .}}{{end}}
</body>
</html>{{end}}
{{define "pager"}}{{if gt .Pages 1}}<nav class="pager" aria-label="{{T "分页"}}">
{{if .HasPrev}}<a href="{{.URL .Prev}}">{{T "‹ 上一页"}}</a>{{else}}<span class="dis">{{T "‹ 上一页"}}</span>{{end}}
{{range .Nums}}{{if eq . $.Page}}<span class="cur">{{.}}</span>{{else}}<a href="{{$.URL .}}">{{.}}</a>{{end}}{{end}}
{{if .HasNext}}<a href="{{.URL .Next}}">{{T "下一页 ›"}}</a>{{else}}<span class="dis">{{T "下一页 ›"}}</span>{{end}}
<span class="hint" style="margin-left:auto">{{.Page}}/{{.Pages}} {{T "页"}}</span>
</nav>{{end}}{{end}}
+2 -1
View File
@@ -270,7 +270,8 @@ footer a{color:var(--muted)}
<footer>
{{T .SiteTitle}} {{T "· 赛博丐帮,行行好"}}<br>
{{if or .AuthorX .AuthorGitHub}}{{T "作者"}}{{if .AuthorX}} <a href="{{.AuthorX}}" target="_blank" rel="noopener">𝕏 @{{lastSeg .AuthorX}}</a>{{end}}{{if .AuthorGitHub}} · <a href="{{.AuthorGitHub}}" target="_blank" rel="noopener">GitHub {{lastSeg .AuthorGitHub}}</a>{{end}}<br>{{end}}
{{T "收款由"}} <a href="{{.RepoURL}}" target="_blank" rel="noopener">BinancePayTool</a> {{T "驱动 · 字体"}} <a href="https://github.com/TakWolf/fusion-pixel-font" target="_blank" rel="noopener">{{T "缝合怪像素字体"}}</a>
{{T "收款由"}} <a href="{{.RepoURL}}" target="_blank" rel="noopener">BinancePayTool</a> {{T "驱动 · 字体"}} <a href="https://github.com/TakWolf/fusion-pixel-font" target="_blank" rel="noopener">{{T "缝合怪像素字体"}}</a>{{if .SourceURL}}<br>
<a href="{{.SourceURL}}" target="_blank" rel="noopener">{{T "本站开源,欢迎 Star ⭐"}}</a>{{end}}
</footer>
</div>
<script>