Terminal reader: play with mpv, mark read on move, folders and pins (#439, #440, #441)

p hands the item's downloaded file (/media/{id}) to mpv in its own window, with the token as a
header. Moving onto an item with j or k marks it read, as selecting one on the page does. The
feed list follows the page's order: pinned first, a subscribed OPML a folder that Space opens,
its unread the sum of its feeds', and P pins or unpins. scripts/build-tui.sh cross-builds the
binary for Linux and macOS under the same cap as the image builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RqkrVARreS9wxzH5o2rF7N
This commit is contained in:
2026-11-11 23:04:02 +00:00
parent 3a3900e959
commit 75d099474c
5 changed files with 265 additions and 14 deletions

52
scripts/build-tui.sh Normal file
View File

@@ -0,0 +1,52 @@
#!/bin/sh
# The terminal reader, `ipx`, for Linux amd64 and Apple silicon Macs, into target/tui/.
#
# sh scripts/build-tui.sh
#
# Cross-compiled with cargo-zigbuild, whose image carries zig as the linker and macOS's SDK, so no
# Mac is needed. The Linux build is musl and static: it runs on any distribution. A release build
# of every dependency is the kind that took Tower down on 2026-11-04, so it runs in a container
# under the same cap as scripts/build-image.sh's builder (6 GB, six threads); if the cap kills it,
# say so and leave the cap be.
set -eu
MEM=6g
CPUS=0-5
NEED_KB=$((9 * 1024 * 1024))
MAX_LOAD=12
IMAGE=ghcr.io/rust-cross/cargo-zigbuild:0.24.1
TARGETS="x86_64-unknown-linux-musl aarch64-apple-darwin"
# Tower's paths, since the Docker daemon is Tower's (see scripts/coverage.sh).
SRC=/mnt/user/vscode/ipx
TARGET=/mnt/fast/vscode/ipx/target-real
cd "$(dirname "$0")/.."
avail=$(awk '/^MemAvailable:/ {print $2}' /proc/meminfo)
load=$(cut -d' ' -f1 /proc/loadavg)
echo "MemAvailable $((avail / 1024)) MB, load $load"
if [ "$avail" -lt "$NEED_KB" ]; then
echo "refusing: under $((NEED_KB / 1024 / 1024)) GB available. Wait, or find what is using it." >&2
exit 1
fi
if awk -v l="$load" -v m="$MAX_LOAD" 'BEGIN { exit !(l > m) }'; then
echo "refusing: load over $MAX_LOAD. Wait for it to come down." >&2
exit 1
fi
# The source read only; the registry and the compiled crates are kept between runs.
docker run --rm --memory "$MEM" --memory-swap "$MEM" --cpuset-cpus "$CPUS" \
-v "$SRC:/work:ro" -v "$TARGET:/target" -v "$TARGET/tui-registry:/usr/local/cargo/registry" \
-e CARGO_TARGET_DIR=/target/tui-build -w /work "$IMAGE" sh -euc "
rustup target add $TARGETS >/dev/null
for t in $TARGETS; do cargo zigbuild --locked --release -p ipx-tui --target \$t; done
mkdir -p /target/tui
# -f replaces a binary someone is running (Text file busy) rather than writing into it.
cp -f /target/tui-build/x86_64-unknown-linux-musl/release/ipx /target/tui/ipx-linux-amd64
cp -f /target/tui-build/aarch64-apple-darwin/release/ipx /target/tui/ipx-macos-arm64
" || {
code=$?
[ "$code" -eq 137 ] && echo "killed at the ${MEM} memory cap: tell the user, do not raise it" >&2
exit "$code"
}
ls -l target/tui