key_help, selector and control each carried their own East Asian width
and truncation logic with slightly different rules, and cli clipped tmux
labels by code point, so a CJK display name ran twice as wide as an
ASCII one before its ellipsis appeared.
Move the cell-width helpers into herdr_hub.text, make truncate_label
cell-aware, and sanitise control characters before curses draws a row
so an escape sequence in a remote error cannot be rendered as ^[ and
misalign the list.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
command_choose ran `herdr session list --json` (up to its 5 s timeout)
before the popup drew anything, contradicting the documented immediate
display. Run the listing on a worker thread and hand the selector a
Future: the popup appears at once with a "… 本机 正在查询" placeholder,
then fills in the local rows and any discovery warning when the result
arrives, mirroring how remote hosts already load.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
configure() ran one tmux client per option and per key binding, and
refresh_labels() one or two per window, on every start/refresh/open: a
no-op refresh with three windows spawned 45 tmux processes. On a phone
each spawn is noticeable.
Add run_batch(), which joins commands with ';' separators (escaping
arguments that end in ';', which tmux would otherwise split) and replays
them one by one if the batch fails so errors stay attributable. Use it
for configure(), window creation, endpoint replacement and label
refreshes. list-windows now also reports the stored short label so
unchanged labels are skipped, and managed_windows() no longer probes
has-session first. The same no-op refresh now spawns 9 processes.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Display names starting with '-' were parsed by tmux rename-window as
flags. The alias was persisted before tmux rejected it, so every later
start/refresh/open failed in refresh_labels until aliases.json was
edited by hand. Guard rename-window with '--', apply the name before
persisting it, and roll back the in-memory table on failure.
- Start the Hub tmux server with -f /dev/null by default. Reading the
user's ~/.tmux.conf let theme status-formats replace the Sessions bar
and root-table bindings leak into Herdr; HERDR_HUB_TMUX_CONFIG still
opts into a custom file.
- An unreadable aliases.json blocked start/refresh/open/choose. Degrade
to default names with a warning (stderr, or the popup message) and
leave the file untouched for `doctor` to report.
- `doctor` crashed with a traceback when a *_BIN override pointed at a
missing executable; report it as a failure instead.
- Refuse to run inside another tmux server before discovery and server
creation, so no unattached Hub is left behind.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The Ctrl+A h command menu gains two host-scoped actions: a plain
`herdr update` that streams updater output into the popup, and an
experimental update-and-hot-restart flow that verifies a live server
handoff and reconnects the originating Hub tab once the popup closes.
Remote updates use the remote host's own proxy settings. A standalone
helper reads literal proxy assignments from shell profiles and running
Clash/Mihomo listeners without executing startup files; Windows reads
process, user and machine proxy variables through PowerShell.