* feat: make ui.pane_borders a three-state auto/always/off mode
Legacy booleans keep parsing (true = auto, false = off); "always" frames
a lone pane while pane_outer_borders is enabled.
* fix: align pane border reload test and documentation
---------
Co-authored-by: Rudolf Schmidt <me@rudionrails.com>
Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com>
* feat(detect): add muse agent with generic Pick blocked detection
Add Muse agent manifests and runtime detection for idle/working/blocked
states. Blocked Pick is generic on footer 'Enter to select' so any
question title/message triggers, fixing missed waiting answer where
previous AND-gated rule required specific hint combos. Cover process
names muse, muse-bin, muse-code variants and YOLO footer.
refs #herdr
* fix(detect): tighten muse manifest and agent lookup
- manifests: change working_esc_interrupt and blocked_approval
from whole_recent to bottom_non_empty_lines(8) to use captured
bottom-buffer controls instead of incidental whole-pane text
- manifests: anchor idle_prompt line_regex to ^\s*⟩\s*$ so working
chrome "⟩ ..." does not match idle
- runtime: restrict muse lookup to explicit aliases
(muse|muse-bin|muse-code|muse-cli), remove starts_with("muse")
fallback that misclassified museum/muse-helper/muser; add
positive/negative identification tests
- keep website mirror identical; verified with agent_detection
manifest check --require-website
refs #2489
refs #2456
* fix(detect): address mjdouglas vendor findings for muse
- manifests: widen pick_request_blocked to bottom_non_empty_lines(8) and add
Enter to toggle for multi-select; add menu_request_blocked for user-opened
pickers (enter confirm/esc go back/esc close/space toggle); add
workspace_trust_blocked for Do you trust dialog; tighten blocked_approval
to footer chrome Allow once/Always allow/Yes proceed; split idle_prompt
into any gate for empty and typed draft and not-gate pick/menu anchors;
change idle_status_fallback from YOLO to model \u00b7 effort \u00b7 cwd shape
- runtime: replace bare muse-bin alias with muse-bin-<version> digit check
to match versioned launcher binary and avoid museum/muse-binary false
positives
- cross-checked with vendor-side captures reported on PR #2489 against
strings in shipped muse-bin binary
refs #2489
* fix(detect): handle path-qualified muse-bin versioned binary
lookup_agent now uses path_basename before matching so a launcher
exec with absolute argv0 like /usr/local/lib/muse/muse-bin-0.1.0-R708.1
is correctly identified. is_muse_versioned_binary also checks basename
so direct path-qualified names succeed without relying on the
argv0_argv fallback path. Prevents missed Muse detection when screen
state and sound overrides are not applied.
refs #2489
* fix(detect): add enter save to muse menu detection
Evidence documents /theme with enter save footer which was not
matched. Add enter save to menu_request_blocked any list and to
idle_prompt not-gate (highlighted row ⟩ <choice> shares glyph) and
keep website mirror in sync. Update evidence comment counts from
four to five.
refs #2489
* docs: add Muse to supported agents table
Muse detection (manifest, process identification, sound)
was added earlier but docs/next agents table omitted the row.
Add Muse as screen manifest / none (no integration hook,
consistent with Amp/Kiro/Maki) to en/ja/zh-cn translations.
* fix(detect): resize agent arrays for new upstream variant
Agent::ALL and SCREEN_MANIFEST_AGENTS gained an entry from master
but kept stale size annotations, breaking compilation after rebase.
refs #2489
* docs: add muse detection entry to next changelog
refs #2489
* docs: add muse sound override to config reference
refs #2489
* fix(detect): prevent false muse blocked states
refs #2489
---------
Co-authored-by: Omer Hamid Kamisli <ohkamisli@poikus.com>
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>
* feat: add per-mode theme overrides
allow [theme.custom.light] and [theme.custom.dark] blocks so custom
overrides can differ per resolved appearance when auto_switch flips.
precedence: theme defaults, then [theme.custom], then the block for
the active mode. configs without the new blocks resolve unchanged.
refs #837
* docs: complete per-mode theme documentation
---------
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>
* feat: keep the outer terminal window title in sync with the session
Herdr emulates the terminals in its panes, so an OSC 0/OSC 2 title
written inside a pane stops at Herdr and never reaches the terminal
Herdr itself runs in. That outer title is what window managers read for
title bars, tab bars, and group bars, so it kept showing whatever the
shell or ssh happened to leave behind.
Add `ui.window_title`, rendered from {hostname}, {workspace}, {tab},
{pane}, and {terminal_title}, and push it to the foreground client
whenever it changes. It renders on the server, so {hostname} names the
machine the panes actually run on rather than the machine a thin remote
client runs on, and it is gated on a pending render so an idle loop
never pays for it.
A title is only remembered as delivered once a foreground client takes
it, so the first client to attach is written to rather than skipped.
`client.window_title.set` still wins over the configured title, and
clearing it now hands the title back to `ui.window_title` instead of
only "herdr".
* fix: deliver the outer window title to a newly attached client
ClientConnected assigns the foreground client directly rather than going
through promote_client_to_foreground, so clearing the sent-title cache
there missed the case that matters most: attaching a second terminal to a
running session. The title was usually unchanged, so the sync returned
early and the new terminal kept whatever its shell or ssh had left.
Key the cache on the client that received the title instead of relying on
every foreground assignment to invalidate it.
* perf: keep hidden pane output off the window title path
Output from a hidden or background pane sets needs_render without setting
needs_full_render, and the retained render plan then skips presentation
for it entirely. Syncing the title on needs_render meant every coalesced
hidden-output tick still formatted, sanitized, and allocated a title that
could not have changed, against the hidden-source early exit AGENTS.md
requires.
Every input to the title is app state, which always requests a full
render, so gate on that instead. The one exception is the focused pane's
own terminal title, which arrives through PTY parsing, so ask for a full
render when that changes and the configured title uses it.
* fix: only cache a window title a client writer received
* fix: carry an api set window title across a live handoff
* fix: make outer window title updates event-driven
---------
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>
* feat: add move tab keybind actions
Add optional keys.move_tab_previous/move_tab_next actions that reorder
the active tab one position, wrapping at either end. Reuses the
existing tab.move runtime path that mouse drag reordering already
drives.
* fix: exit navigate mode on single-tab move attempts
* feat: show a zoom indicator in the desktop tab bar
Reserve the right edge of the tab row for a ZOOM pill while the
focused pane is zoomed, matching the accent style of the mode bars.
The per-tab Z suffix stays; the pill makes the zoomed state visible
at a glance like tmux's status-right flag.
* feat: optionally show the hostname in the desktop tab bar
Add ui.tab_bar_hostname to display the machine's hostname at the right
edge of the tab row, like tmux's #h in status-right. The value resolves
where the server renders, so remote sessions show the remote host. Off
by default.
* fix: strip control characters from the tab bar hostname
* fix: hide the hostname when it would squeeze out the tab strip
* feat: add configurable tab bar status
* fix: harden tab bar status updates
* fix: terminate tab bar status process trees
* fix: disable status commands on unsupported platforms
* fix: skip unchanged status command renders
* fix: keep tab bar status opt-in by default
---------
Co-authored-by: David Heinemeier Hansson <david@hey.com>
* feat: switch IME to ASCII in prefix mode on Windows
Extend switch_ascii_input_source_in_prefix to Windows: detect IME state
via WM_IME_CONTROL and toggle to ASCII with SendInput for the Korean IME.
Other IME languages are detected and left unchanged.
refs #1802
* docs: clarify Windows (Korean IME) support for prefix ASCII switching
Follow-up to the Windows prefix ASCII switch, which extended
switch_ascii_input_source_in_prefix to Windows but left the surrounding
text describing macOS only.
- Settings row label: "(macOS)" -> "(macOS/Windows)", plus its test.
- Generalize the Windows behavior wording to "switches the IME to
English (ASCII) input" in the config-reference.json entry and the
model.rs doc comment, and state that Windows support is currently
limited to the Korean IME.
- Add a :::caution aside noting the Korean-IME-only limitation to the
en/ja/zh preview docs, keeping the three translations in sync.
refs #1802
* fix: warn when prefix IME toggle injection is incomplete
SendInput can queue fewer events than requested (for example when blocked
by UIPI), which would otherwise leave the IME silently stuck in ASCII or
native mode. Log a warning with the sent and expected counts so the failure
is diagnosable.
refs #1802
* fix: guard prefix IME restore and correct open-status naming
Address maintainer review on the Windows prefix ASCII switch:
- Restore only when safe: store the foreground window at switch time and,
before toggling back, require that the same window is still focused and
the IME is still in English. This stops restore from toggling another
application or overriding a manual return to Hangul during prefix mode.
- Report injection success from send_vk_tap and only arm restoration when
the initial toggle landed; on a partial injection retry the dropped
key-up so a key is not left logically held down. The injection policy is
split into send_vk_tap_with(inject) so the success, partial, and failure
branches are unit-tested without the real SendInput.
- Correct the WM_IME_CONTROL sub-command naming: 0x0005 is IMC_GETOPENSTATUS
(IME open state), not IMC_GETCONVERSIONMODE. Behavior is unchanged; the
constant, comments, and tests now describe the open-status read.
refs #1802
* fix: bound IME open-status read with a timeout
The prefix IME switch reads IMC_GETOPENSTATUS via WM_IME_CONTROL, which
crosses into the terminal-emulator process. A plain SendMessageW would block
herdr's client thread until that process responds, indefinitely if it hangs.
Read through SendMessageTimeoutW with SMTO_ABORTIFHUNG and a short timeout
instead; on timeout the switch is skipped and restore does nothing, so a hung
host process can no longer freeze prefix-mode entry or exit.
refs #1802
* fix: preserve windows ime restoration after partial injection
refs #1802
---------
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>