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>
Opening a window whose target was removed from the registry (its tab
stays open by design) and failing landed in the action menu with a
"重试连接" row that indexed self.entries directly and raised KeyError,
killing the popup with a traceback.
Only offer the retry action for registered targets, degrade a stale
retry row to a message, and keep the popup alive when a handler raises
anything unexpected instead of dying with a traceback.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>