diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 45a698b..e98a732 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -103,3 +103,5 @@ jobs: files: | artifacts/termdown-* artifacts/SHA256SUMS + install.sh + uninstall.sh diff --git a/CLAUDE.md b/CLAUDE.md index 7f29a30..efa195e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,3 +28,13 @@ If `make check` fails, fix the underlying issue — do not bypass with `#[allow( ### When you add a new build/lint/test command Add it as a Makefile target first, then reference the target from CI. Never let CI and local commands diverge. + +## Git workflow + +**Do not commit or push to `master` directly.** All changes land on `master` via PR merges. For any code or docs change: + +1. Create a feature branch (e.g. `feat/...`, `fix/...`, `docs/...`). +2. Commit there, push the branch, open a PR. +3. Never run `git commit` while `HEAD` is on `master`, and never run `git push origin master` — even for "small" doc tweaks. Committing to local master (even without pushing) tends to contaminate the merge base of later feature branches. + +If you notice you're on `master` with uncommitted changes, stash them, switch to a new branch, and pop the stash before committing. diff --git a/README.md b/README.md index 13e4b1d..98aedf5 100644 --- a/README.md +++ b/README.md @@ -4,40 +4,77 @@ Render Markdown with large-font headings in the terminal using the Kitty graphics protocol. + + + + + +
termdown rendering the Chinese READMEtermdown --tui rendering the English README
+ ## Motivation Inspired by [glow](https://github.com/charmbracelet/glow) and [mdfried](https://github.com/benjajaja/mdfried). glow is a great terminal Markdown renderer, but headings are only distinguished by ANSI bold/color -- they can't actually be displayed at a larger size. mdfried supports image-rendered headings, but requires entering a TUI. -termdown aims to be **a lightweight `cat`-like tool where headings are truly rendered large**. It rasterizes H1-H3 text as PNG images and displays them via the Kitty graphics protocol -- no TUI, pipe-friendly, just direct output. +termdown rasterizes H1-H3 headings as PNG and paints them via the Kitty graphics protocol. Two modes share the same renderer: -## Terminal Support +- **Direct output** -- `cat`-like, pipe-friendly; dump rendered Markdown straight into your terminal. +- **Interactive TUI** (`--tui`) -- vim-style browser with search, Table of Contents, and link-follow navigation for longer documents. -Requires a terminal with **Kitty graphics protocol** support: +H4-H6 headings always fall back to ANSI bold text. -- [Ghostty](https://ghostty.org) -- [Kitty](https://sw.kovidgoyal.net/kitty/) -- [WezTerm](https://wezfurlong.org/wezterm/) -- [iTerm2](https://iterm2.com) +## Installation -On unsupported terminals, termdown will print a warning and heading images may not display correctly. H4-H6 headings always render as plain ANSI bold text. +### Install script -## Installation +```sh +curl -fsSL https://raw.githubusercontent.com/rrbe/termdown/master/install.sh | bash +``` + +Defaults to `/usr/local/bin`. Override the target directory with `TERMDOWN_INSTALL_DIR`. + +
+Manual download (no script) + +```sh +TARGET=aarch64-apple-darwin +BASE="https://github.com/rrbe/termdown/releases/latest/download" + +curl -LO "${BASE}/termdown-${TARGET}.tar.gz" +curl -LO "${BASE}/SHA256SUMS" +grep "termdown-${TARGET}.tar.gz" SHA256SUMS | shasum -a 256 -c - + +tar xzf "termdown-${TARGET}.tar.gz" +sudo mv termdown /usr/local/bin/ +``` + +
+ +### Install from source -### From source +```sh +cargo install --git https://github.com/rrbe/termdown +``` + +Installs into `~/.cargo/bin/`. + +## Uninstall ```sh -cargo install --path . +curl -fsSL https://raw.githubusercontent.com/rrbe/termdown/master/uninstall.sh | bash ``` -### Build manually +
+Manual uninstall ```sh -cargo build --release -cp target/release/termdown /usr/local/bin/ +rm $(which termdown) +rm -rf ~/.termdown ``` +
+ ## Usage ```sh @@ -50,7 +87,7 @@ cat notes.md | termdown # Use a specific theme instead of auto-detect termdown --theme light README.md -# Flags +# View help termdown --help termdown --version ``` @@ -94,18 +131,18 @@ termdown reads configuration from `~/.termdown/config.toml`. theme = "auto" [font.heading] -# Font for Latin/English text in H1-H3 headings (sans-serif recommended) +# English heading font (sans-serif recommended) latin = "Inter" -# Font for CJK text in H1-H3 headings +# CJK heading font cjk = "LXGW WenKai" -# Optional emoji / symbol fallback font for image-rendered headings +# Emoji / symbol fallback font for image-rendered headings (optional) emoji = "Apple Color Emoji" ``` Headings with mixed scripts (e.g. "Hello 世界") will render each character with the appropriate font automatically. -Standalone emoji in H1-H3 headings are also supported through font fallback when the selected emoji font exposes outline or raster glyphs. +Standalone emoji in H1-H3 headings are also rendered via font fallback where possible. > **Note:** Body text is rendered as plain ANSI text -- its font is determined by your terminal emulator settings, not by termdown. To change the body font, configure your terminal directly. @@ -131,14 +168,16 @@ If no config file exists, termdown uses platform-specific defaults and falls bac | Songti SC | Noto Serif | Microsoft YaHei | | STSong | DejaVu Serif | | -## Uninstall +## Terminal Support -Remove the binary and delete the configuration directory: +Requires a terminal with **Kitty graphics protocol** support: -```sh -rm $(which termdown) -rm -rf ~/.termdown -``` +- [Ghostty](https://ghostty.org) +- [Kitty](https://sw.kovidgoyal.net/kitty/) +- [WezTerm](https://wezfurlong.org/wezterm/) +- [iTerm2](https://iterm2.com) + +On unsupported terminals, termdown will print a warning and heading images may not display correctly. H4-H6 headings always render as plain ANSI bold text. ## Known Issues diff --git a/README_CN.md b/README_CN.md index bf13095..96f11e0 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,41 +1,79 @@ # Termdown -在终端中以大字体标题渲染 Markdown,基于 Kitty 图形协议。 +在终端中以大字体标题渲染 Markdown,让观感更接近 GUI Markdown 阅读器的体验,基于 Kitty 图形协议。 + + + + + + +
termdown 渲染中文 READMEtermdown --tui 渲染英文 README
## 为什么做这个 本项目受 [glow](https://github.com/charmbracelet/glow) 和 [mdfried](https://github.com/benjajaja/mdfried) 启发。 -glow 是优秀的终端 Markdown 渲染器,但标题只能用 ANSI 粗体/颜色区分,无法真正放大显示。mdfried 支持图片化标题,但需要进入 TUI 模式。 +- **glow** 不支持放大标题字体 +- **mdfried** 支持放大 markdown 标题,但个人感觉可以做的更美观一点 -termdown 的目标很简单:**像 `cat` 一样轻量,但标题能真正大写化显示**。它将 H1-H3 标题栅格化为 PNG 图片,通过 Kitty 图形协议直接输出到终端,无需 TUI,管道友好。 +termdown 将 H1-H3 标题栅格化为 PNG 图片,通过 Kitty 图形协议直接绘制到终端。提供两种使用模式: -## 终端支持 +- **直接输出** —— `termdown README.md`, 像 `cat` 一样轻量、管道友好,把渲染后的 Markdown 直接打到终端。适合快速查看短文档。 +- **交互式 TUI** —— `termdown --tui README.md`,类 vim/less 的体验,支持常见的翻页、搜索等快捷键,支持查看 TOC、链接跳转,适合阅读较长文档。 -需要支持 **Kitty 图形协议** 的终端: +H4-H6 标题始终以 ANSI 粗体文本渲染。不想让文档加入那么多种字重,那样反而损害可读性。 -- [Ghostty](https://ghostty.org) -- [Kitty](https://sw.kovidgoyal.net/kitty/) -- [WezTerm](https://wezfurlong.org/wezterm/) -- [iTerm2](https://iterm2.com) +## 安装 -不支持的终端会打印警告。H4-H6 标题始终以 ANSI 粗体文本渲染。 +### 安装脚本 -## 安装 +```sh +curl -fsSL https://raw.githubusercontent.com/rrbe/termdown/master/install.sh | bash +``` + +默认装到 `/usr/local/bin`。用 `TERMDOWN_INSTALL_DIR` 覆盖安装目录。 + +
+手动下载 + +```sh +TARGET=aarch64-apple-darwin +BASE="https://github.com/rrbe/termdown/releases/latest/download" + +curl -LO "${BASE}/termdown-${TARGET}.tar.gz" +curl -LO "${BASE}/SHA256SUMS" +grep "termdown-${TARGET}.tar.gz" SHA256SUMS | shasum -a 256 -c - + +tar xzf "termdown-${TARGET}.tar.gz" +sudo mv termdown /usr/local/bin/ +``` + +
+ +### 源码安装 -### 从源码安装 +```sh +cargo install --git https://github.com/rrbe/termdown +``` + +安装到 `~/.cargo/bin/`。 + +## 卸载 ```sh -cargo install --path . +curl -fsSL https://raw.githubusercontent.com/rrbe/termdown/master/uninstall.sh | bash ``` -### 手动构建 +
+手动卸载 ```sh -cargo build --release -cp target/release/termdown /usr/local/bin/ +rm $(which termdown) +rm -rf ~/.termdown ``` +
+ ## 使用 ```sh @@ -45,7 +83,7 @@ termdown README.md # 从 stdin 管道输入 cat notes.md | termdown -# 指定主题(不使用自动检测) +# 指定主题(不使用终端亮色、暗色主题自动检测) termdown --theme light README.md # 查看帮助 @@ -63,22 +101,22 @@ termdown --tui README.md 按键绑定: -| 按键 | 动作 | -|---|---| -| `j` / `↓` | 向下滚动一行 | -| `k` / `↑` | 向上滚动一行 | -| `d` / `u` | 半屏向下 / 向上 | -| `f` / `Space` / `PgDn` | 整屏向下 | -| `b` / `PgUp` | 整屏向上 | -| `gg` / `G` | 跳到文档开头 / 末尾 | -| `]` / `[` | 下一个 / 上一个标题 | -| `t` | 切换目录面板 | -| `/` | 正向搜索 | -| `n` / `N` | 下一个 / 上一个匹配 | -| `?` | 切换快捷键帮助弹窗 | -| `Enter` | 打开链接(屏幕中有多个链接时显示序号选择器) | -| `o` / `i` | 在已跳转的 `.md` 文档之间前进 / 后退 | -| `q` / `Ctrl-C` | 退出 | +| 按键 | 动作 | +| ---------------------- | -------------------------------------------- | +| `j` / `↓` | 向下滚动一行 | +| `k` / `↑` | 向上滚动一行 | +| `d` / `u` | 半屏向下 / 向上 | +| `f` / `Space` / `PgDn` | 整屏向下 | +| `b` / `PgUp` | 整屏向上 | +| `gg` / `G` | 跳到文档开头 / 末尾 | +| `]` / `[` | 下一个 / 上一个标题 | +| `t` | 切换目录面板 | +| `/` | 正向搜索 | +| `n` / `N` | 下一个 / 上一个匹配 | +| `?` | 切换快捷键帮助弹窗 | +| `Enter` | 打开链接(屏幕中有多个链接时显示序号选择器) | +| `o` / `i` | 在已跳转的 `.md` 文档之间前进 / 后退 | +| `q` / `Ctrl-C` | 退出 | TUI 模式需要指定文件路径,不支持从 stdin 读取。 @@ -113,39 +151,40 @@ H1-H3 标题中的单个 emoji 也会通过 fallback 字体尽量渲染出来。 **Latin**(无衬线): -| macOS | Linux | Windows | -|-------|-------|---------| -| Avenir | Inter | Segoe UI | -| Avenir Next | Noto Sans | Arial | -| Futura | DejaVu Sans | Verdana | -| Helvetica Neue | Liberation Sans | | +| macOS | Linux | Windows | +| -------------- | --------------- | -------- | +| Avenir | Inter | Segoe UI | +| Avenir Next | Noto Sans | Arial | +| Futura | DejaVu Sans | Verdana | +| Helvetica Neue | Liberation Sans | | **CJK**: -| macOS | Linux | Windows | -|-------|-------|---------| -| Noto Serif CJK SC | Noto Serif CJK SC | SimSun | -| Source Han Serif SC | Source Han Serif SC | KaiTi | -| Songti SC | Noto Serif | Microsoft YaHei | -| STSong | DejaVu Serif | | +| macOS | Linux | Windows | +| ------------------- | ------------------- | --------------- | +| Noto Serif CJK SC | Noto Serif CJK SC | SimSun | +| Source Han Serif SC | Source Han Serif SC | KaiTi | +| Songti SC | Noto Serif | Microsoft YaHei | +| STSong | DejaVu Serif | | -## 卸载 +## 终端支持 -删除二进制文件和配置目录: +需要支持 **Kitty 图形协议** 的终端(目前仅在 Ghostty 和 iTerm2 上测试过): -```sh -rm $(which termdown) -rm -rf ~/.termdown -``` +- [Ghostty](https://ghostty.org) +- [Kitty](https://sw.kovidgoyal.net/kitty/) +- [WezTerm](https://wezfurlong.org/wezterm/) +- [iTerm2](https://iterm2.com) + +不支持的终端会打印警告。H4-H6 标题始终以 ANSI 粗体文本渲染。 ## 已知问题 - **换行显示** -- 含 ANSI 转义序列的长行可能无法正确换行 -- **终端兼容** -- 目前仅在 Ghostty 和 iTerm2 上测试过,其他支持 Kitty 协议的终端可能表现不一致 - **字体选择与降级** -- 字体粗细匹配依赖平台 API(Core Text / fontconfig),不一定能解析到预期的字重变体 - **主题检测** -- 自动检测依赖终端对 OSC 11 的响应;如终端不支持,请通过 `--theme` 或配置文件手动指定主题 - **复杂 emoji 序列** -- 依赖 ZWJ 的复杂 emoji(例如家庭/群组类组合、部分肤色组合)目前仍可能拆成多个字形,因为标题渲染还没有完整文本 shaping -- **TUI 帮助弹窗与标题图片** -- TUI 模式下 `?` 帮助弹窗绘制在文字层,而标题图片位于 Kitty graphics 层(始终覆盖在文字之上)。与弹窗区域重叠的标题图片会在弹窗打开时被临时移除,关闭后自动恢复 —— 这是 Kitty graphics 协议的限制,不是 bug +- **TUI 帮助弹窗与标题图片** -- TUI 模式下 `?` 帮助弹窗绘制在文字层,而标题图片位于 Kitty graphics 层(始终覆盖在文字之上)。与弹窗区域重叠的标题图片会在弹窗打开时被临时移除,关闭后自动恢复 —— 这是 Kitty graphics 协议的限制 ## 许可证 diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..5cf9b71 --- /dev/null +++ b/TODO.md @@ -0,0 +1,3 @@ +- [ ] 测试 html 标签支持 +- [ ] 图片支持 +- [ ] 长文本换行时缩进的处理 diff --git a/docs/screenshots/termdown_render_cn_demo.png b/docs/screenshots/termdown_render_cn_demo.png new file mode 100644 index 0000000..0d70657 Binary files /dev/null and b/docs/screenshots/termdown_render_cn_demo.png differ diff --git a/docs/screenshots/termdown_render_en_tui_demo.png b/docs/screenshots/termdown_render_en_tui_demo.png new file mode 100644 index 0000000..4370cb0 Binary files /dev/null and b/docs/screenshots/termdown_render_en_tui_demo.png differ diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..d43182c --- /dev/null +++ b/install.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# termdown installer +# +# Downloads the prebuilt binary for your platform from GitHub Releases, +# verifies its SHA-256 checksum, and installs it. Never invokes sudo; if the +# install directory is not writable, prints a clear hint and exits. +# +# Usage: +# curl -fsSL https://raw.githubusercontent.com/rrbe/termdown/master/install.sh | bash +# +# Environment variables: +# TERMDOWN_INSTALL_DIR install directory (default: /usr/local/bin) + +set -euo pipefail + +REPO="rrbe/termdown" +INSTALL_DIR="${TERMDOWN_INSTALL_DIR:-/usr/local/bin}" +BASE="https://github.com/${REPO}/releases/latest/download" + +info() { printf '\033[1;32m==>\033[0m %s\n' "$*"; } +warn() { printf '\033[1;33mwarning:\033[0m %s\n' "$*" >&2; } +err() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; } + +OS="$(uname -s)" +ARCH="$(uname -m)" + +case "$OS" in + Darwin) OS_STR=apple-darwin ;; + Linux) OS_STR=unknown-linux-gnu ;; + *) + err "Unsupported OS: $OS (only macOS and Linux are supported by this script)." + err "For Windows, download the archive manually from https://github.com/${REPO}/releases." + exit 1 + ;; +esac + +case "$ARCH" in + x86_64|amd64) ARCH_STR=x86_64 ;; + arm64|aarch64) ARCH_STR=aarch64 ;; + *) err "Unsupported architecture: $ARCH"; exit 1 ;; +esac + +TARGET="${ARCH_STR}-${OS_STR}" +ARCHIVE="termdown-${TARGET}.tar.gz" + +info "Installing termdown (${TARGET})" + +TMP="$(mktemp -d)" +trap 'rm -rf "$TMP"' EXIT + +info "Downloading ${ARCHIVE}" +curl -fsSL -o "${TMP}/${ARCHIVE}" "${BASE}/${ARCHIVE}" +curl -fsSL -o "${TMP}/SHA256SUMS" "${BASE}/SHA256SUMS" + +info "Verifying checksum" +if command -v shasum >/dev/null 2>&1; then + CHECK_CMD="shasum -a 256 -c -" +elif command -v sha256sum >/dev/null 2>&1; then + CHECK_CMD="sha256sum -c -" +else + err "Neither 'shasum' nor 'sha256sum' is available; cannot verify the download." + exit 1 +fi +(cd "$TMP" && grep " ${ARCHIVE}\$" SHA256SUMS | $CHECK_CMD >/dev/null) + +info "Extracting" +tar -xzf "${TMP}/${ARCHIVE}" -C "$TMP" + +mkdir -p "$INSTALL_DIR" 2>/dev/null || true +DEST="${INSTALL_DIR%/}/termdown" +if ! install -m 0755 "${TMP}/termdown" "$DEST" 2>/dev/null; then + err "Cannot write to ${INSTALL_DIR} (permission denied)." + cat >&2 <&2 + ;; +esac + +"$DEST" --version diff --git a/uninstall.sh b/uninstall.sh new file mode 100755 index 0000000..ea979d4 --- /dev/null +++ b/uninstall.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# termdown uninstaller +# +# Removes the installed binary and deletes the config directory (~/.termdown). +# Never invokes sudo; if the binary's location is not writable, prints a clear +# hint and exits. +# +# Usage: +# curl -fsSL https://raw.githubusercontent.com/rrbe/termdown/master/uninstall.sh | bash +# +# Environment variables: +# TERMDOWN_INSTALL_DIR location of the binary (default: auto-detect via `command -v`) +# TERMDOWN_KEEP_CONFIG set to 1 to keep ~/.termdown (default: remove it) + +set -euo pipefail + +REPO="rrbe/termdown" + +info() { printf '\033[1;32m==>\033[0m %s\n' "$*"; } +err() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; } + +if [ -n "${TERMDOWN_INSTALL_DIR:-}" ]; then + BIN="${TERMDOWN_INSTALL_DIR%/}/termdown" +else + BIN="$(command -v termdown 2>/dev/null || true)" +fi + +if [ -z "$BIN" ] || [ ! -e "$BIN" ]; then + info "termdown binary not found — nothing to remove" +else + info "Removing ${BIN}" + if ! rm -f "$BIN" 2>/dev/null; then + err "Cannot remove ${BIN} (permission denied)." + cat >&2 <