Files
terminalX/vendor/libghostty-spm/Script/generate-themes.sh
kid aa92d0e676 初始提交:terminalX 可运行态(M0/M1/M1.5 已真机验证)
- M0: libghostty SSH 终端(渲染/输入/连接)+ 白屏修复(OutputGate) + 会话状态机/自动重连
- M1: tsnet 用户态组网 + SSH-over-tsnet(fd 桥),shell 级真机验证;R5(Go+gvisor+C+++Swift 同进程) retire
- M1.5: tmux -CC 原生 tab(MVP)
- 结构: packages/(TXCore·TXTransport), apps/TerminalX, vendor/(libghostty-spm/libssh2/mbedtls/tsnet-bridge), artifacts/
- 文档: CLAUDE.md + docs/HANDOFF.md(新会话入口)
- 环境: 认证代理→依赖 vendor 本地化;Go 在 ~/.local/go;仅模拟器/未签名
- 待续: M2 mosh, tmux 多 pane, M4 安全(host key/SE)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 10:20:46 +08:00

196 lines
6.5 KiB
Bash
Executable File

#!/bin/zsh
set -euo pipefail
# Downloads all Ghostty theme files from iTerm2-Color-Schemes and generates
# Swift source files for the GhosttyTheme library.
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
OUTPUT_DIR="$ROOT_DIR/Sources/GhosttyTheme/Themes"
TEMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TEMP_DIR"' EXIT
mkdir -p "$OUTPUT_DIR"
# Fetch the list of theme files from GitHub API
echo "[+] fetching theme list from github api"
curl -sL "https://api.github.com/repos/mbadolato/iTerm2-Color-Schemes/contents/ghostty" \
| python3 -c "
import json, sys
data = json.load(sys.stdin)
for item in data:
if item['type'] == 'file':
print(item['name'])
" > "$TEMP_DIR/theme_list.txt"
THEME_COUNT=$(wc -l < "$TEMP_DIR/theme_list.txt" | tr -d ' ')
echo "[+] found $THEME_COUNT themes"
# Download all themes
echo "[+] downloading themes"
mkdir -p "$TEMP_DIR/themes"
while IFS= read -r name; do
encoded_name=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$name'))")
curl -sL "https://raw.githubusercontent.com/mbadolato/iTerm2-Color-Schemes/master/ghostty/$encoded_name" \
-o "$TEMP_DIR/themes/$name" &
# Limit concurrent downloads
if (( $(jobs -r | wc -l) >= 20 )); then
wait -n
fi
done < "$TEMP_DIR/theme_list.txt"
wait
echo "[+] downloaded all themes"
# Generate Swift files using Python
echo "[+] generating swift source files"
python3 - "$TEMP_DIR/themes" "$OUTPUT_DIR" "$TEMP_DIR/theme_list.txt" << 'PYEOF'
import os, sys, re, string
themes_dir = sys.argv[1]
output_dir = sys.argv[2]
list_file = sys.argv[3]
with open(list_file) as f:
theme_names = [line.strip() for line in f if line.strip()]
def parse_theme(path):
result = {}
palette = {}
with open(path) as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
if '=' not in line:
continue
key, _, value = line.partition('=')
key = key.strip()
value = value.strip()
if key == 'palette':
# format: N=#RRGGBB or N=RRGGBB
idx_str, _, color = value.partition('=')
idx = int(idx_str.strip())
color = color.strip().lstrip('#')
palette[idx] = color
else:
# strip # from color values
clean_val = value.lstrip('#')
result[key] = clean_val
result['_palette'] = palette
return result
def swift_identifier(name):
"""Convert theme name to a valid Swift identifier."""
# Replace + with Plus before stripping
ident = name.replace('+', 'Plus')
# Replace non-alphanumeric with underscore
ident = re.sub(r'[^a-zA-Z0-9]', '_', ident)
# Collapse multiple underscores
ident = re.sub(r'_+', '_', ident)
# Strip leading/trailing underscores
ident = ident.strip('_')
# Prefix with underscore if starts with digit
if ident and ident[0].isdigit():
ident = '_' + ident
# camelCase: lowercase first char
if ident:
ident = ident[0].lower() + ident[1:]
return ident
def swift_string(s):
return '"' + s.replace('\\', '\\\\').replace('"', '\\"') + '"'
def optional_field(data, ghostty_key, swift_type="String"):
val = data.get(ghostty_key)
if val:
return swift_string(val)
return "nil"
# Group themes by first letter
groups = {}
all_themes = []
for name in sorted(theme_names, key=str.lower):
path = os.path.join(themes_dir, name)
if not os.path.isfile(path):
continue
try:
data = parse_theme(path)
except Exception as e:
print(f" [-] skipping {name}: {e}", file=sys.stderr)
continue
bg = data.get('background')
fg = data.get('foreground')
if not bg or not fg:
print(f" [-] skipping {name}: missing background/foreground", file=sys.stderr)
continue
ident = swift_identifier(name)
first_char = name[0].upper()
if first_char not in string.ascii_uppercase:
first_char = 'Symbols'
palette = data.get('_palette', {})
palette_str = '['
for i in sorted(palette.keys()):
palette_str += f'{i}: {swift_string(palette[i])}, '
if palette_str.endswith(', '):
palette_str = palette_str[:-2]
palette_str += ']'
entry = {
'name': name,
'ident': ident,
'background': swift_string(bg),
'foreground': swift_string(fg),
'cursorColor': optional_field(data, 'cursor-color'),
'cursorText': optional_field(data, 'cursor-text'),
'selectionBackground': optional_field(data, 'selection-background'),
'selectionForeground': optional_field(data, 'selection-foreground'),
'palette': palette_str,
'group': first_char,
}
groups.setdefault(first_char, []).append(entry)
all_themes.append(entry)
# Write per-group Swift files
for group, entries in sorted(groups.items()):
filename = f"Themes_{group}.swift"
filepath = os.path.join(output_dir, filename)
with open(filepath, 'w') as f:
f.write("// Auto-generated by Script/generate-themes.sh — do not edit\n\n")
f.write("public extension GhosttyThemeDefinition {\n")
for e in entries:
f.write(f" static let {e['ident']} = GhosttyThemeDefinition(\n")
f.write(f" name: {swift_string(e['name'])},\n")
f.write(f" background: {e['background']},\n")
f.write(f" foreground: {e['foreground']},\n")
f.write(f" cursorColor: {e['cursorColor']},\n")
f.write(f" cursorText: {e['cursorText']},\n")
f.write(f" selectionBackground: {e['selectionBackground']},\n")
f.write(f" selectionForeground: {e['selectionForeground']},\n")
f.write(f" palette: {e['palette']}\n")
f.write(f" )\n\n")
f.write("}\n")
print(f" [+] wrote {filename} ({len(entries)} themes)")
# Write catalog file
catalog_path = os.path.join(output_dir, "ThemeCatalog_Generated.swift")
with open(catalog_path, 'w') as f:
f.write("// Auto-generated by Script/generate-themes.sh — do not edit\n\n")
f.write("public extension GhosttyThemeCatalog {\n")
f.write(" static let allThemes: [GhosttyThemeDefinition] = [\n")
for e in all_themes:
f.write(f" .{e['ident']},\n")
f.write(" ]\n")
f.write("}\n")
print(f" [+] wrote ThemeCatalog_Generated.swift ({len(all_themes)} themes)")
print(f"[+] generated {len(all_themes)} themes total")
PYEOF
echo "[+] done"