#!/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 <種類> 印出 版型風格來源(project/global/default/builtin) # html-style.sh set <種類> <版型> <風格> [--project|--global] 寫入設定(預設 --global) # html-style.sh unset <種類> [--project|--global] 移除設定 # html-style.sh list 印出合併後的所有設定:種類版型風格來源 # html-style.sh layouts 列出可用版型:名稱繁中說明 # html-style.sh styles 列出可用風格:名稱繁中說明 # html-style.sh key wiki <頁名> 推導 wiki 頁的種類:取第一個底線前的字首 # html-style.sh key issue / <編號> [--labels <名稱>[,<名稱>]] # 推導議題的種類:依序試每個標籤,第一個讓 get 回 project 或 global 的標籤勝出; # 都對不到就回 ISSUE:DEFAULT。已經有標籤名單時用 --labels 帶進來,省掉一次 API 呼叫。 # key 的輸出固定一行:{種類}{依據} # 依據 = 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 / <編號> [--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=種類 -> 版型風格來源 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///; 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