Files
gitea/tools/html-style.sh
jiantw83 2b759542a8 feat(gitea): 新增種類推導指令與技能盤點頁型
範本種類以前由模型每次重推。底線怎麼切、標籤照什麼順序試,都是固定的
輸入輸出,卻放在技能內文裡由模型自己來,有人切錯底線,也有人跳過標籤
順序。現在把規則寫進腳本,輸出固定一行,還附上是哪個字首或哪個標籤產生
的依據。已經有標籤名單時可以直接帶進來,省掉一次 API 呼叫。

技能盤點頁是新的頁面類型。加進 wiki 存取庫的允許清單與規則驗證腳本,
這種頁才有地方可放,解析規則也才有人驗。
2026-08-31 11:11:55 +08:00

228 lines
8.9 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-style.sh — 「哪一種 wiki 頁或議題,用哪一種版型與風格出 HTML」的設定(供 jsc-gitea:html-style、html-export 使用)。
#
# 設定格式(一行一筆):{種類}={版型},{風格}
# 種類:WIKI:{頁名前綴}(例 WIKI:PLAN)、ISSUE:{標籤名}(例 ISSUE:bug)、DEFAULT(都對不到時用)
# 「#」開頭為註解,空白行忽略。同一種類出現多行時取最後一行。
# 解析順序:
# 1. 目前工作目錄的 ./.jsc/html-styles(專案覆寫)
# 2. $JSC_HOME/html-styles.conf(JSC_HOME 預設 ~/.jsc)
# 3. 種類對不到就退 DEFAULT,DEFAULT 也沒有才用內建預設 report,minimal
#
# 用法:
# html-style.sh get <種類> 印出 版型<TAB>風格<TAB>來源(project/global/default/builtin)
# html-style.sh set <種類> <版型> <風格> [--project|--global] 寫入設定(預設 --global)
# html-style.sh unset <種類> [--project|--global] 移除設定
# html-style.sh list 印出合併後的所有設定:種類<TAB>版型<TAB>風格<TAB>來源
# html-style.sh layouts 列出可用版型:名稱<TAB>繁中說明
# html-style.sh styles 列出可用風格:名稱<TAB>繁中說明
# html-style.sh key wiki <頁名> 推導 wiki 頁的種類:取第一個底線前的字首
# html-style.sh key issue <owner>/<repo> <編號> [--labels <名稱>[,<名稱>]]
# 推導議題的種類:依序試每個標籤,第一個讓 get 回 project 或 global 的標籤勝出;
# 都對不到就回 ISSUE:DEFAULT。已經有標籤名單時用 --labels 帶進來,省掉一次 API 呼叫。
# key 的輸出固定一行:{種類}<TAB>{依據}
# 依據 = prefix:{字首}、label:{標籤名}、fallback:no-labels、fallback:no-configured-label
#
# 結束碼: 0=成功 1=取不到議題標籤,或設定檔的目錄建不出來 2=用法錯誤 4=版型或風格沒有對應範本檔
#
# 陷阱:
# - get 永遠印得出一組值:對不到就退 DEFAULT,再對不到就退內建預設。呼叫端不必自己準備退路,
# 但要看第三欄,才知道這組值是使用者設的還是撿來的。
# - key 的推導規則放在腳本裡,不放技能內文:字首怎麼切、標籤怎麼試,是固定的輸入輸出,
# 每次由模型重推就會有人切錯底線或跳過標籤順序。
# - set 會先確認範本檔真的存在,擋掉打錯字的版型或風格;設定寫得進去、出圖卻失敗最難查。
set -u
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
plugin_root="${CLAUDE_PLUGIN_ROOT:-$script_dir/..}"
TPL="$plugin_root/templates/html"
JSC_HOME="${JSC_HOME:-$HOME/.jsc}"
PROJECT_FILE=./.jsc/html-styles
GLOBAL_FILE="$JSC_HOME/html-styles.conf"
BUILTIN_LAYOUT=report
BUILTIN_STYLE=minimal
usage() {
cat >&2 <<'EOF'
用法:
html-style.sh get <種類>
html-style.sh set <種類> <版型> <風格> [--project|--global]
html-style.sh unset <種類> [--project|--global]
html-style.sh list | layouts | styles
html-style.sh key wiki <頁名>
html-style.sh key issue <owner>/<repo> <編號> [--labels <名稱>[,<名稱>]]
種類: WIKI:{頁名前綴}、ISSUE:{標籤名}、DEFAULT
結束碼: 0=成功 1=取不到議題標籤或建不出目錄 2=用法錯誤 4=版型或風格沒有對應範本檔
EOF
exit 2
}
read_value() { # $1=設定檔 $2=種類 -> 「版型,風格」
[ -f "$1" ] || return 0
awk -F= -v want="$2" '
{ sub(/\r$/, "") }
/^[ \t]*#/ { next }
/^[ \t]*$/ { next }
index($0, "=") == 0 { next }
{
key = $1
gsub(/[ \t]/, "", key)
if (key != want) next
val = substr($0, index($0, "=") + 1)
gsub(/[ \t]/, "", val)
if (val != "") v = val
}
END { if (v != "") print v }
' "$1"
}
resolve() { # $1=種類 -> 版型<TAB>風格<TAB>來源
for _f in "$PROJECT_FILE:project" "$GLOBAL_FILE:global"; do
_file=${_f%:*}; _src=${_f##*:}
_v=$(read_value "$_file" "$1")
if [ -n "$_v" ]; then
printf '%s\t%s\t%s\n' "${_v%%,*}" "${_v##*,}" "$_src"
return 0
fi
done
if [ "$1" != DEFAULT ]; then
_d=$(resolve DEFAULT)
_src=$(printf '%s' "$_d" | cut -f3)
# DEFAULT 自己也沒設定時,來源照實說是 builtin,不要蓋成 default
[ "$_src" = builtin ] || _src=default
printf '%s\t%s\n' "$(printf '%s' "$_d" | cut -f1,2)" "$_src"
return 0
fi
printf '%s\t%s\tbuiltin\n' "$BUILTIN_LAYOUT" "$BUILTIN_STYLE"
}
# 範本檔第一行註解就是繁中說明:版型放在 <!-- 說明 -->,風格放在 /* 說明 */。
describe() { # $1=檔案
head -n1 "$1" 2>/dev/null | sed 's/<!--[[:space:]]*//; s/[[:space:]]*-->//; s|/\*[[:space:]]*||; s|[[:space:]]*\*/||'
}
list_layouts() {
for f in "$TPL"/layout/*.html; do
[ -f "$f" ] || continue
name=$(basename "$f" .html)
printf '%s\t%s\n' "$name" "$(describe "$f")"
done
}
list_styles() {
for f in "$TPL"/style/*.css; do
[ -f "$f" ] || continue
name=$(basename "$f" .css)
printf '%s\t%s\n' "$name" "$(describe "$f")"
done
}
write_kv() { # $1=檔案 $2=種類 $3=值
dir=$(dirname "$1")
mkdir -p "$dir" || { echo "[jsc][HTML 設定][ERR]:建不出目錄「$dir」。" >&2; exit 1; }
tmp="$1.tmp.$$"
{ [ -f "$1" ] && grep -v "^[[:space:]]*$2[[:space:]]*=" "$1" || true; } > "$tmp"
[ -n "$3" ] && printf '%s=%s\n' "$2" "$3" >> "$tmp"
mv "$tmp" "$1"
}
cmd="${1:-}"; [ -n "$cmd" ] || usage
shift || true
case "$cmd" in
layouts) list_layouts; exit 0 ;;
styles) list_styles; exit 0 ;;
list)
keys=$( { [ -f "$PROJECT_FILE" ] && cut -d= -f1 "$PROJECT_FILE" || true
[ -f "$GLOBAL_FILE" ] && cut -d= -f1 "$GLOBAL_FILE" || true; } \
| sed 's/^[[:space:]]*//; s/[[:space:]]*$//' | grep -v '^#' | grep -v '^$' | sort -u)
[ -n "$keys" ] || { printf 'DEFAULT\t%s\t%s\tbuiltin\n' "$BUILTIN_LAYOUT" "$BUILTIN_STYLE"; exit 0; }
printf '%s\n' "$keys" | while IFS= read -r k; do
printf '%s\t%s\n' "$k" "$(resolve "$k")"
done
exit 0 ;;
get)
key="${1:-}"; [ -n "$key" ] || usage
resolve "$key"
exit 0 ;;
key)
kind="${1:-}"; [ -n "$kind" ] || usage
shift
case "$kind" in
wiki)
page="${1:-}"; [ -n "$page" ] || usage
# 頁名前綴 = 第一個底線之前那一段;沒有底線就是整個頁名。
prefix=${page%%_*}
printf 'WIKI:%s\tprefix:%s\n' "$prefix" "$prefix"
exit 0 ;;
issue)
repo="${1:-}"; idx="${2:-}"
[ -n "$repo" ] && [ -n "$idx" ] || usage
shift 2
labels=''
have_labels=0
case "${1:-}" in
--labels) [ "$#" -ge 2 ] || usage; labels="$2"; have_labels=1 ;;
'') ;;
*) usage ;;
esac
if [ "$have_labels" = 0 ]; then
raw=$("$script_dir/issue.sh" labels-of "$repo" "$idx") || {
echo "[jsc][HTML 設定][ERR]:取不到議題 $repo#$idx 的標籤。" >&2; exit 1; }
labels=$(printf '%s' "$raw" | tr '\n' ',')
fi
matched=''
old_ifs=$IFS
IFS=','
for l in $labels; do
[ -n "$l" ] || continue
# 標籤順序就是優先序:第一個使用者真的設過的標籤勝出,
# 撿來的 DEFAULT 不算數,否則第一個標籤永遠贏,後面的設定形同虛設。
case "$(resolve "ISSUE:$l" | cut -f3)" in
project|global) matched="$l"; break ;;
esac
done
IFS=$old_ifs
if [ -n "$matched" ]; then
printf 'ISSUE:%s\tlabel:%s\n' "$matched" "$matched"
elif [ -z "$(printf '%s' "$labels" | tr -d ',')" ]; then
printf 'ISSUE:DEFAULT\tfallback:no-labels\n'
else
printf 'ISSUE:DEFAULT\tfallback:no-configured-label\n'
fi
exit 0 ;;
*) usage ;;
esac ;;
set)
key="${1:-}"; layout="${2:-}"; style="${3:-}"
[ -n "$key" ] && [ -n "$layout" ] && [ -n "$style" ] || usage
shift 3
target="$GLOBAL_FILE"; scope=global
case "${1:-}" in
--project) target="$PROJECT_FILE"; scope=project ;;
--global|'') ;;
*) usage ;;
esac
[ -f "$TPL/layout/$layout.html" ] || {
echo "[jsc][HTML 設定][ERR]:沒有版型「$layout」。可用:$(list_layouts | cut -f1 | tr '\n' ' ')" >&2; exit 4; }
[ -f "$TPL/style/$style.css" ] || {
echo "[jsc][HTML 設定][ERR]:沒有風格「$style」。可用:$(list_styles | cut -f1 | tr '\n' ' ')" >&2; exit 4; }
write_kv "$target" "$key" "$layout,$style"
printf '已寫入 %s:%s=%s,%s(%s)\n' "$target" "$key" "$layout" "$style" "$scope"
exit 0 ;;
unset)
key="${1:-}"; [ -n "$key" ] || usage
shift
target="$GLOBAL_FILE"
case "${1:-}" in
--project) target="$PROJECT_FILE" ;;
--global|'') ;;
*) usage ;;
esac
[ -f "$target" ] || { echo "找不到設定檔:$target" >&2; exit 0; }
write_kv "$target" "$key" ''
printf '已移除 %s 的 %s\n' "$target" "$key"
exit 0 ;;
*) usage ;;
esac