Files
gitea/tools/html-render.sh
jiantw83andClaude Opus 5 a0c13f807a feat(gitea): 新增 HTML 匯出與範本風格設定
What:新增 html-export 與 html-style 兩支技能、tools/html-render.sh 與 tools/html-style.sh 兩支工具,以及六種版型乘五種風格的 HTML 範本。

Why:wiki 頁與議題要拿給 Gitea 以外的人看時,只能複製 markdown;不同類型的文件也該有各自的版面,不是每份都長一樣。

How:版型(report、slide、dashboard、spec、timeline、onepager)決定內容怎麼排,風格(minimal、corporate、dark、print、vivid)決定看起來長怎樣,兩者自由搭配。哪一種頁面套哪一組由設定決定:專案的 .jsc/html-styles 優先,其次 $JSC_HOME/html-styles.conf,對不到退 DEFAULT,再對不到才用內建的 report/minimal。產出是單一 HTML 檔,CSS 與腳本全部內嵌。

Who:需要把 wiki 頁或議題寄給客戶、主管或跨團隊同事的人。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 09:19:25 +08:00

112 lines
4.6 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env sh
# html-render.sh — 把 markdown 套上版型與風格,輸出單一 HTML 檔(供 jsc-gitea:html-export 使用)。
#
# 為什麼要有這支腳本:markdown 轉 HTML 交給 Gitea 自己渲染(gitea.sh markdown),出來的排版才跟
# wiki、議題頁看到的一致;版型與風格則是固定的字串替換。兩件事都有標準輸入輸出,不必每次重寫。
#
# 用法:
# html-render.sh --markdown <檔案> --title <標題> --out <輸出檔>
# [--subtitle <副標>] [--layout <版型>] [--style <風格>]
# [--source-url <來源網址>]
#
# --layout 預設 report,--style 預設 minimal。可用清單見 html-style.sh layouts / styles。
#
# 輸出: 寫出 --out 指定的 HTML 檔,並在標準輸出印出該檔路徑。
# 結束碼: 0=成功 1=渲染或寫檔失敗 2=用法錯誤 4=找不到版型或風格範本
#
# 陷阱:
# - HTML 是單一檔案,CSS 直接內嵌,不外連任何資源;產出物常常是寄給別人看的,外連在對方那裡會破圖。
# - markdown 渲染走 Gitea API。連不上就失敗收場,不自己拼一套半套的轉換——半套轉換出來的表格
# 跟 wiki 上看到的不一樣,比失敗更難發現。
# - 渲染端點不吃 wiki 情境,`[[頁名]]` 這種 wiki 內部連結會原樣留著。呼叫端要先換成絕對網址,
# 產出的 HTML 才連得回去。
set -eu
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
plugin_root="${CLAUDE_PLUGIN_ROOT:-$script_dir/..}"
TPL="$plugin_root/templates/html"
GITEA="$script_dir/gitea.sh"
usage() {
cat >&2 <<'EOF'
用法:
html-render.sh --markdown <檔案> --title <標題> --out <輸出檔>
[--subtitle <副標>] [--layout <版型>] [--style <風格>]
[--source-url <來源網址>]
結束碼: 0=成功 1=渲染或寫檔失敗 2=用法錯誤 4=找不到版型或風格範本
EOF
exit 2
}
md=''; title=''; out=''; subtitle=''; layout=report; style=minimal; source_url=''
while [ "$#" -gt 0 ]; do
case "$1" in
--markdown) [ "$#" -ge 2 ] || usage; md="$2"; shift 2 ;;
--title) [ "$#" -ge 2 ] || usage; title="$2"; shift 2 ;;
--out) [ "$#" -ge 2 ] || usage; out="$2"; shift 2 ;;
--subtitle) [ "$#" -ge 2 ] || usage; subtitle="$2"; shift 2 ;;
--layout) [ "$#" -ge 2 ] || usage; layout="$2"; shift 2 ;;
--style) [ "$#" -ge 2 ] || usage; style="$2"; shift 2 ;;
--source-url) [ "$#" -ge 2 ] || usage; source_url="$2"; shift 2 ;;
*) echo "[jsc][HTML 產生][ERR]:不認得的選項「$1」。" >&2; usage ;;
esac
done
[ -n "$md" ] && [ -n "$title" ] && [ -n "$out" ] || usage
[ -f "$md" ] || { echo "[jsc][HTML 產生][ERR]:找不到 markdown 檔「$md」。" >&2; exit 2; }
layout_file="$TPL/layout/$layout.html"
style_file="$TPL/style/$style.css"
[ -f "$layout_file" ] || { echo "[jsc][HTML 產生][ERR]:找不到版型範本「$layout_file」。" >&2; exit 4; }
[ -f "$style_file" ] || { echo "[jsc][HTML 產生][ERR]:找不到風格範本「$style_file」。" >&2; exit 4; }
# markdown -> HTML 片段:交給 Gitea 自己渲染,排版才跟站上一致。
fragment=$(mktemp)
trap 'rm -f "$fragment"' EXIT
if ! "$GITEA" markdown "$md" > "$fragment" 2>/dev/null || [ ! -s "$fragment" ]; then
echo '[jsc][HTML 產生][ERR]:Gitea 的 markdown 渲染失敗,這次不出檔。請確認 GITEA_HOST 與權杖後重跑。' >&2
exit 1
fi
python3 - "$layout_file" "$style_file" "$fragment" "$out" "$title" "$subtitle" "$source_url" "$layout" "$style" "$TPL" <<'PY'
import html, sys, datetime, os
layout_file, style_file, frag_file, out_file, title, subtitle, source_url, layout, style, tpl_dir = sys.argv[1:11]
def read(p):
with open(p, encoding='utf-8') as f:
return f.read()
page = read(layout_file)
css = read(style_file)
base_css = read(os.path.join(tpl_dir, 'base.css'))
base_js = read(os.path.join(tpl_dir, 'base.js'))
content = read(frag_file)
generated = datetime.datetime.now().astimezone().strftime('%Y-%m-%d %H:%M')
source_html = ''
if source_url:
safe = html.escape(source_url, quote=True)
source_html = '<a class="source" href="%s" rel="noreferrer">來源:%s</a>' % (safe, safe)
for key, value in (
('{{TITLE}}', html.escape(title)),
('{{SUBTITLE}}', html.escape(subtitle)),
('{{BASE}}', base_css),
('{{BASE_JS}}', base_js),
('{{STYLE}}', css),
('{{CONTENT}}', content),
('{{SOURCE}}', source_html),
('{{GENERATED}}', generated),
('{{LAYOUT}}', html.escape(layout)),
('{{STYLE_NAME}}', html.escape(style)),
):
page = page.replace(key, value)
with open(out_file, 'w', encoding='utf-8') as f:
f.write(page)
PY
printf '%s\n' "$out"