Skip to content

Commit 2854d5d

Browse files
committed
fix: make the pane menu's stack action work, and stop calling it a split
- Added the missing `Stack` arm to `apply_context_menu_action_via_api`, the dispatcher every production menu activation funnels through, so selecting it now creates a pane instead of silently closing the menu - Covered that path with a test that fails with one pane when the arm is removed, because the pre-existing test asserted against the `cfg(test)` dispatch mirror and stayed green while the real binary was broken - Renamed the context menu item from `Split stacked` to `Stack`, since a stack is not a split - Renamed the navigate bar entry to `stack` and the keybind help entry to `stack pane` - Surfaced the stack action in the navigate bar, which previously offered only the two splits - Added a TestBackend assertion that the navigate bar renders all three ways to add a pane, each with a bound key docs: document stacked panes wherever splitting is documented - Documented the stack layout node (`panes`, `active`) in socket-api so clients can round-trip a stacked tab through layout.export and layout.apply - Added stacking to the pane concepts, quick-start and keyboard pages in English, Japanese and Simplified Chinese - Added the `split_stacked` binding to the keyboard and configuration samples in all three locales - Corrected the `pane split` and `plugin pane open` usage strings to read `right|down|stacked`, which the parsers already accepted - Taught the embedded agent skill to reach for `--direction stacked` when another split would leave panes unusably small - Left `pane move --split` as `right|down` because the server rejects moving a pane into a stack chore: import fork-local justfile recipes An optional import keeps local recipes in an ignored directory, so the tracked justfile carries one line instead of a growing fork-only block and a rebase has one place to conflict. fix(input): give stack title rows priority over the split resize handle A stack's first title row sits on the region's top border, which is the same row a vertical split above it uses as its resize handle. Border lookup runs before the pane hit test, so clicking the top member of a stack below a split started a resize instead of focusing it, leaving that member unreachable by mouse. feat(ui): offer a stacked split in the pane menu The pane menu offered split right and split down, so stacking was reachable only from the keyboard. The placement enum already had the variant, so this is wiring rather than new capability. fix(server): keep hidden stack members out of their own title rows The retained renderer applied every pane's dirty patch at its inner rect. A collapsed member's inner rect is its one row title row, and collecting a patch has no size check, so asking for one row handed back the first row of that hidden terminal to be painted over the member's own name. Only the span between the corners was overwritten, which is why the frame survived while the name did not, and only while that agent was producing output. A collapsed member also reports its whole screen dirty for as long as it stays hidden, because nothing else refreshes a hidden pane's render state, so one stack was enough to drop a whole tab back to full redraws. Skipping it fixes both, and matches what pane rendering and kitty graphics already do. feat(ui): draw stacked pane titles on the frame Each member of a stack was named twice: once on the box's top border and again on a title row of its own directly below it. The first member's title row is now the region's own top border, so a stack costs one row less than a box plus a row per member, and the name sits where a pane's name already sits. A title row is drawn as a ruled frame line opening its own corner rather than tee-ing into one continuous box, so a stack reads as a deck of cards rather than as one pane chopped into strips. The region's top border row is shared with the pane above and is left to the border pass, which has already drawn its junctions and its colour; only the title text goes there. A collapsed member takes a muted version of the accent instead of the generic border grey, so a stack reads as one group of related panes. StackSlot carries the region's borders because only the visible member holds them, and rescanning the pane list for it would be quadratic inside a render loop. docs: document stacked panes and the stacked split keybind Covers prefix+shift+s and the agent panel row change in the sample config, the keyboard page and the config reference. feat(ui): name panes after the agent session A pane's name is now resolved in one place, TerminalState::display_name: a title the agent pushed, then an explicit rename, then the agent session name, then the terminal's own title. Pane chrome, stack title rows and the agent panel all read it, so they cannot drift. Two of them already had, which is how a renamed session showed on a stack row but not in the panel. An agent renaming its own session arrives as a terminal title, so leaving that out of the chain made a renamed session look unnamed. The agent session name reaches the panel through the existing pane token, and the default second row is now agent then pane name. pi prefixes its title with a lone glyph the same way claude does, so it joins the set that is stripped before a title is shown. An unnamed pi session is nothing but that glyph, which now leaves no name and falls back to the agent label. feat(input): add a stacked split action prefix+shift+s puts a new pane in the focused pane's stack, creating one if the pane is not stacked yet. It sits alongside split_vertical and split_horizontal as a third way to place a pane, and stays prefix-first so herdr does not start swallowing keys from shells and editors. Moving between members needs no new action: collapsed title rows are ordinary rects stacked vertically, so prefix+j and prefix+k already walk a stack. SplitDirection gains a stacked variant, which the cli accepts as herdr pane split --direction stacked. It has no split axis, so the conversion to a layout direction is now fallible, and pane move rejects it before taking the source pane out of its tab rather than after. feat(ui): render stacked pane title rows Every member of a stack gets a title row, the visible one included, and its terminal starts on the row directly below. Without a row of its own the last collapsed title sat right above the visible terminal and read as that terminal's label. The box is drawn once around the whole region and carried by the visible member, so the existing border joining code handles it like any other pane. Collapsed rows carry no chrome. Chrome is decided for the region rather than per member, otherwise the top, middle and bottom of a stack get different borders and the visible pane's terminal changes height as focus moves. Kitty graphics bypass the cell buffer, so a collapsed member is skipped when collecting placements too; otherwise a hidden pane draws images over its own title row. A layout with no stacks costs one scan and no allocation. feat(layout): add a stacked pane node Panes in a stack share one region. One shows its terminal and the rest collapse to a single title row, so a tab with six agents no longer gives each of them a sixth of the screen. Unlike zellij, a collapsed pane keeps a full size terminal behind its title row. Every member is sized to the region the visible member occupies, so moving through a stack never resizes a pty, never reflows a running agent, and leaves the bottom buffer that detection reads intact. The visible member also keeps a floor of content rows, so adding members drops title rows rather than shrinking every terminal toward nothing. Stack membership is a session fact, so it is saved with the layout and reported by the layout API. Restore drops dead members, keeps the visible one in range, and turns a stack that is down to one pane back into a plain pane. A stack holds panes rather than subtrees, and applying a layout restores the member the caller marked visible, so exporting a layout and applying it back reproduces the same stack. fix: speed up alternate-screen history reads (#2426) refs #2387 fix: keep active sidebar rows visible (#2794) refs #2792 feat(cli): guide agents to Herdr resources fix(docs): publish channel-aware documentation indexes fix(docs): apply llms patch during website builds feat(docs): publish agent-readable documentation fix: reject prompts to blocked agents (#2790) * fix: reject prompts to blocked agents refs #2788 * docs: clarify agent prompt timing refs #2788 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> fix: detect all claude half-circle spinner frames (#2762) refs #2760 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> fix: wait for new pane shell readiness (#2774) * fix: wait for new pane shell readiness refs #2773 * refactor: share agent startup bounds refs #2773 * fix: reconcile agent names after startup timeout refs #2773 * fix: cover agent cleanup scheduling skew refs #2773 * fix: bound pane shell readiness retries refs #2773 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> fix: keep tab and sidebar clicks when the terminal reports a stray drag (#2736) * fix: keep tab and sidebar clicks when the terminal reports a stray drag This works around a Ghostty bug that causes herdr tabs to be unclickable in fullscreen mode. Ghostty on macOS in native fullscreen emits one motion report after a fresh press in the strip where the menu bar reveals, with the row shifted to near the bottom of the grid. The cause is upstream: macOS delivers one mouse event whose y is nearly a window height away from the real pointer, and Ghostty reports it as it arrives (iTerm2 and Terminal.app report the same clicks correctly). Herdr treated the stray report as a real drag and lost the click two ways: it opened a tab reorder with no drop target, making the release a no-op, and it handed the release to the pane under the bogus coordinates. Now, a press on the tab bar or sidebar owns the gesture until release, and a tab drag only starts once the pointer is over a real drop target. refs #796 * fix: scope chrome mouse gestures to their input source * fix: preserve chrome drag input source * fix: preserve concurrent chrome presses --------- Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> fix(graphics): support high-dpi direct file frames (#2680) fix: preserve logical lines in scrollback editor (#2735) refs #2733 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Can Celik <ogulcancelik@gmail.com> feat: add qwen code detection and restore (#2743) refs #2730 fix(agent): focus Copilot before prompting (#2734) * fix(agent): focus Copilot before prompting refs #1698 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(agent): cover stale focus-reporting state refs #1698 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> fix: strip claude title spinner frames (#2709) * fix: source title activity glyphs from manifests refs #2707 * fix: simplify manifest-driven title stripping refs #2707 * fix: reconcile handoff title policies refs #2707 * fix: strip claude title spinner frames refs #2707 * fix: keep claude detection manifest unchanged --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> fix: use the running binary in agent hooks (#2722) fix: preserve workspace labels for root repositories (#2727) refs #2594 chore: update contributor list perf: avoid repeated Git config reads (#2688) refs #2643 Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix: synchronously terminate cancelled status commands fix: detect claude 2.1.228 half-circle busy spinner fix: disambiguate shifted punctuation keybinds (#2676) refs #2674 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix(render): compact large terminal redraws (#2675) refs #2670 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> fix(windows): reduce idle agent detection CPU (#2651) * fix(windows): reduce idle agent detection CPU refs #2642 * fix(windows): gate agent process discovery refs #2642 --------- Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com> fix: make just build cross-platform (#2647) Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix: derive repo name for embedded bare layouts (#2660) refs #2657 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> chore: refresh english documentation fix: wait for agent prompt readiness (#2537) * fix: wait for agent prompt readiness refs #2410 * fix: return pane updates after tab bar command refs #2410 --------- Co-authored-by: Can Celik <ogulcancelik@gmail.com> feat: keep the outer terminal window title in sync with the session (#2627) * 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> refactor(website): clean up homepage fix(input): ignore modified j/k/up/down in session navigator (#2377) The Char('j')/Char('k')/Down/Up match arms in handle_navigator_key had no modifier guard, unlike every sibling arm in the same match block, so Ctrl+K, Alt+J, etc. also moved the navigator selection. refs #1981 Co-authored-by: Can Celik <ogulcancelik@gmail.com> ci: remove automatic issue gate fix(windows): strip elevated terminal title decoration (#2632) fix(server): prioritize shutdown requests (#2624) refs #2612 test(windows): serialize media player environment access (#2619) fix: track opencode tui session selection (#2455) refs #2450 Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> fix(ui): anchor the host cursor to modal name inputs (#2569) * fix(ui): anchor the host cursor to modal name inputs IMEs draw their composition preview at the host terminal cursor. The rename and new-worktree dialogs drew a block glyph instead of setting one, so the frame carried no cursor and the client kept the position the focused pane last reported. Japanese composition appeared behind the dialog and only reached the field on Enter. Set the cursor to the caret column instead, counting wide characters as two cells. refs #1755 * fix(ui): keep the clamped caret cell blank A name that fills the field left the clamped caret sitting on the last rendered glyph. A terminal inverts the cell under its cursor and an IME composes there, so the glyph was hidden and composition overlapped it. Render the text one column short of the field. The field is still cleared in full, so the clamped caret always lands on a blank cell. refs #1755 --------- Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com> Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix: center tab labels for symmetric highlight padding (#2570) * fix: center tab labels for symmetric highlight padding * docs: note centered tab labels in the changelog * fix: center tab labels by display width, not char count feat: add move tab keybind actions (#2561) * 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: add configurable tab bar status (#2586) * 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> test: guard pane-scaled render performance refs #2550 feat: add direct pane resize keybindings (#2558) fix(client): avoid abort on terminal hangup (#2427) * fix(client): avoid abort on terminal hangup refs #2424 * test(client): gate PTY integration on unix refs #2424 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Can Celik <ogulcancelik@gmail.com> perf(ui): avoid full pane mode reads for scrollbars (#2554) fix(input): preserve alt-prefixed control keys (#2543) refs #2514 fix(website): improve plugins page readability on dark ground fix(ui): reclaim scrollbar gutter in alternate screen (#2538) feat(graphics): add direct pane frame streaming (#2523) * feat(graphics): add direct pane frame streaming * feat(graphics): expose pane visibility * test(graphics): preserve legacy terminal image output * fix(graphics): harden streaming lifecycle * fix(input): preserve mouse releases outside panes feat: make outer pane borders configurable (#2535) fix: make windows installer swaps atomic (#2530) refs #2356 feat(input): add per-pane right-click routing (#2504) chore: approve dhh as contributor fix: forward pane terminal bells (#2498) refs #2453 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> feat: support all agent integrations on windows (#2496) fix(windows): share Git Bash agent candidate scans (#2494) refs #2459 fix(config): report unknown theme names (#2488) refs #2452 fix: avoid redundant Windows recent-history snapshots (#2474) refs #962 Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix(input): preserve shift in kitty alternate reports (#2479) refs #2435 fix(remote): restore blocking bridge streams (#2485) refs #2478 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> feat(website): rebuild compare page in new design with runtime positioning fix: verify stable release checksums fix: publish stable release checksums chore: update website feat(marketplace): track star history for trending and new plugin surfaces fix(theme): refresh host appearance on focus (#2417) * fix(theme): refresh host appearance on focus refs #2416 * fix(input): preserve split appearance replies refs #2416 * fix(input): retain fragmented appearance reports refs #2416 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix(detect): ignore Cursor Run Everything status (#2220) * fix(detect): ignore Cursor Run Everything status refs #1763 * fix(detect): constrain Cursor approval controls refs #1763 * test(detect): remove cursor manifest behavior fixture --------- Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> fix: add positive idle detection for kiro-cli prompt (#2301) * fix: add positive idle detection for kiro-cli prompt refs discussion #982 * fix: remove manifest integration tests per reviewer request --------- Co-authored-by: Can Celik <ogulcancelik@gmail.com> feat(copy-mode): add big-word motions (#2270) Co-authored-by: Ubuntu <ubuntu@ip-172-31-252-234.ca-west-1.compute.internal> Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix(terminal): restore keyboard reporting on detach (#2395) refs #2393 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> fix(detect): recognize versioned Python agent wrappers (#2188) Co-authored-by: Can Celik <ogulcancelik@gmail.com> docs: document custom agent integrations fix(cli): accept --flag=value and reordered options in pane read/wait-output (#2183) * fix(cli): accept --flag=value and reordered options in pane read/wait-output * test(cli): trim redundant pane parser coverage --------- Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix: keep plugin marketplace counts current fix: render OSC 4 palette overrides instead of forwarding the index (#2162) Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix: avoid ctrl-tab escape sequences in legacy panes refs #2296 fix(ci): repair contributor gate handling chore: update canonical repository references ci: restore approved contributor gate fix(sidebar): highlight active agent in collapsed sidebar (#2382) The collapsed sidebar renders agent rows with an unconditional Style::default().fg(p.overlay0), so the active agent is indistinguishable from the others. The workspace list directly above it, and render_agent_detail in the expanded sidebar, both mark the active entry. Reuse the existing active treatment: resolve the active entry with the same is_active_pane call the expanded agent panel uses, then paint the row background with surface_dim and the position number with text, the same tokens the collapsed workspace rows already use. Assisted-By: devx/717e5259-915f-4a7b-af07-6abe46da5b90 feat(windows): support remote attach to unix hosts (#2329) * feat(windows): support remote attach to unix hosts * fix(remote): preserve unix bridge behavior fix(website): update author X profile chore: remove project-local worktree extension refs #2325 docs: refresh readme and sponsors feat(theme): add optional sidebar background fix(config): accept retired agent panel scope (#2295) refs #2292 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix(input): preserve modifyOtherKeys key releases (#2303) refs #2302 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix(ci): make issue gate structural fix(terminal): render halfwidth katakana voiced marks (#2257) Co-authored-by: oyoguhito <oyoguhito@kamatanoMacBook-Pro.local> Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix(layout): return focus to the pane a split was opened from (#2266) Closing a focused pane handed focus to the next pane in tree order. For a pane opened beside another one -- a plugin split, a file viewer, any transient tool pane -- that is rarely where the user was: it lands on some unrelated neighbour rather than the pane that opened it. Track the pane focus came from in TileLayout and prefer it when the focused pane closes, falling back to tree order when there is no history, when it points at the pane being closed, or when it points at a pane that has since gone away. The history lives in the layout, so it can only ever name a pane in the same tab. A one-slot history is only sound if internal focus excursions never write it, so the tree edits that used to bounce focus around now go through target-taking primitives instead. close_pane removes a background pane directly, so detach_pane and take_pane_for_move stop focus-close-refocusing. split_pane splits a target without moving focus: the runtime split path only focuses the new pane once the spawn succeeds, which makes a failed split a pure rollback, and the targeted and unfocused workspace split paths stop fabricating history. insert_pane_near now takes the focus intent, so an unfocused pane move leaves the target tab's history alone. The layout-level focused-split helpers become test-only; production splits all flow through the target-taking path. Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix(ui): search single-tab names in navigator (#2320) fix(input): parse default mouse reports (#2312) * fix(input): parse default mouse reports refs #2309 * fix(input): preserve split default mouse reports refs #2309 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix(cli): resolve pane query --current from caller (#2298) refs #2297 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> fix(input): keep pending url clicks across host focus loss (#2291) Opening a URL raises the browser, which takes focus away from the host terminal before the mouse release arrives. release_input_source(_headless) cleared pending_url_click_sources on that focus loss, so the release was forwarded to the pane and the agent opened the same URL again. Clear the set in clear_input_source instead, whose only production caller is remove_client. refs #2290 Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com> feat(ui): add distinct status indicators (#2282) refs #2260 fix: detect claude confirmation prompts refs #2268 fix(website): copy heading links to clipboard fix(ui): preserve sidebar scroll across clients (#2280) refs #2255 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> fix(input): preserve shift-tab in pane automation (#2259) refs #1561 fix(docs): allow corrections to published versions refs #916 feat(plugins): index marketplace manifests docs: publish preview documentation fix(ui): keep collapsed workspace status visible (#2239) refs #2216 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Can Celik <ogulcancelik@gmail.com> docs: refine cpu optimization blog post docs: update website manifest for v0.8.0 release: v0.8.0 docs: finalize 0.8.0 release documentation feat(website): rebuild marketing pages on new brand chassis fix: page keys scroll pane scrollback at zsh and REPL prompts (#2191) * fix: page keys scroll pane scrollback at zsh and REPL prompts PageUp/PageDown were forwarded to the pane whenever DECCKM (application cursor) was on, assuming only primary-screen pagers enable it, but zsh's line editor also enables DECCKM, as do REPLs such as python3. The result was PageUp scrolling shell history instead of Herdr scrollback. Bracketed paste discriminates the two: it means the app accepts typed or pasted text at a prompt, so line editors enable it and pagers do not. * test: update page key state fixture --------- Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix(pi): restrict state reporting to tui sessions (#2159) Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix: propagate host color scheme to pane apps (#2214) refs #714 fix(input): preserve hover during extended-button drags fix(docs): support older git in snapshot checks fix(docs): publish only released documentation fix: avoid blocking on foreground cwd checks (#2213) refs #2206 fix(cli): report non-UTF-8 arguments instead of panicking (#2207) * fix(cli): report non-UTF-8 arguments instead of panicking * fix(cli): avoid echoing malformed arguments --------- Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> fix(client): query host cell size when the ioctl reports no pixels (#2160) fix: preserve claude settings formatting (#2089) refs #2066 Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> fix: stop advertising worktree json flag (#2174) refs #2171 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> fix(detect): recognize opencode2 as opencode refs #2169 fix: route vscode remote copies through osc52 (#2173) refs #2015 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> fix(windows): detect agents across git bash exec boundaries (#2170) refs #2107 fix(socket_paths): remove dead if-branch in derive_client_socket_from_api_socket (#2157) fix(input): decode 0x1f as Ctrl+_ not Ctrl+- per ASCII standard (#2165) * fix(input): decode 0x1f as Ctrl+_ not Ctrl+- per ASCII standard refs #2164 * fix(input): update control-byte matrix test for 0x1f -> Ctrl+_ feat: add antigravity-cli integration (#2087) * feat: add antigravity_cli integration harness Register the `antigravity_cli` target, resolve its configuration directory to `~/.gemini/antigravity-cli/`, wire target actions, and add lifecycle hook scripts for Unix and Windows. * feat: support session resume for antigravity-cli * fix: correct antigravity-cli hook config path, shape, and agent label Antigravity CLI reads global customizations from ~/.gemini/config, keys hooks.json by hook name, and only accepts the matcher/hooks wrapper for the tool events. The previous install wrote event arrays at the top level of ~/.gemini/antigravity-cli/hooks.json, which agy never reads and would reject anyway, so no Herdr hook ever ran. Session reports were dropped for a third reason: the hooks reported the agent as antigravity-cli, but the server normalizes that to the canonical label agy before matching, so is_official_agent_source never matched and agy --conversation resume was unreachable. - Resolve the config dir to ~/.gemini/config - Nest Herdr entries under a Herdr-owned "herdr" block that install rewrites and uninstall removes, leaving other named hooks untouched - Emit grouped entries for PreToolUse/PostToolUse and flat handler lists for PreInvocation/PostInvocation/Stop - Report the canonical agy label from both hook assets and match it in agent_resume - Compile-gate the hook asset constants and stop shadowing $args in the PowerShell hook - Document the real directory, that it must already exist, and the named-block behavior refs #1011 refs #1571 * fix: make antigravity-cli integration session-only antigravity cli cannot express lifecycle safely: there is no blocked event, postinvocation is skipped on interruption, and stop fires at the end of a turn rather than on process exit, which left stale state and released authority at the wrong time. report only the conversation on preinvocation and let screen detection own agent state. resume via `agy --conversation <id>` is unchanged. * fix: update antigravity session after conversation switch --------- Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix(ci): skip pr gate in forks feat(ui): make pane scrollbars optional (#2166) refs #2167 fix: expand copy-mode word-end window past a final wide glyph (#2145) point_is_final_atom compared an atom's terminating column against a word-motion target's starting column. The two are equal for narrow cells but never for wide ones, so the read window stopped expanding and copy-mode `e` halted at the soft-wrap boundary on CJK text. Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix: report a clear cli error when no herdr server is running (#1963) * fix: report a clear cli error when no herdr server is running socket cli commands surfaced a raw io::Error debug string (`Error: Os { code: 2, ... }`) when nothing was listening on the api socket, which read like a bad --cwd path. map dead-socket connect failures to a `server_not_running` json error carrying the resolved socket path, printed once at the edge that surfaces the error; recovering callers (plugin offline registry fallback, agent start polling) recognize the marker and keep their existing behavior. refs #1941 * fix: map dead-socket errors on the unchecked cli request path refs #1941 * fix: make server-not-running guidance session-aware refs #1941 fix(windows): preserve semantic escape key taps fix(input): preserve native key lifecycle across routing (#2142) refs #2077 Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com> chore: remove experiments from settings tui feat: support bottom tab bar placement (#2118) * feat: support bottom tab bar placement * fix: preserve mouse cleanup under bottom mode bar refs #2117 chore: update agentmd for protocol changes fix: read alternate-screen agent history fix(sidebar): keep worktree groups packed fix(client): restore terminal on sighup and sigterm Enable termination-signal handling so SIGHUP and SIGTERM follow the graceful quit path and restore terminal state. Log handler registration failures and add PTY coverage for direct SIGHUP and server EOF restoration Co-authored-by: Matt Coles <macols@amazon.com> fix: preserve windows sessions after ssh logout (#2098) * fix: preserve windows sessions after ssh logout refs #2008 * chore: document windows wmi safety refs #2008 fix(omp): support Windows session paths (#2092) fix: resume agents after headless restore (#2088) refs #2064 ci: consolidate ai review trigger label fix: launch argument-free agents on windows (#2075) refs #2072 fix: add opt-in child-group process detection (#2052) * fix: add opt-in child-group process detection refs #1982 * fix: preserve lifecycle probes without foreground groups refs #1982 fix: preserve kitty associated text (#2051) refs #2020 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> chore: finish repository organization migration Update rose pine surface_dim colour to "Overlay" (#2002) Co-authored-by: Can Celik <ogulcancelik@gmail.com> chore: update repository links after organization transfer ci: allow repository organization migration fix: scope agent skill installation refs #2022 docs: update preview manifest fix(windows): preserve parent agent ownership refs #1514 fix: preserve windows path variables during install refs #1947 test(windows): remove flaky sound timing assertion refs #1330 fix(windows): restore system notifications and sound playback (#2019) ci: remove approved contributor bypass chore: approve merged contributor we11adam fix: preserve navigate mode after pane focus (#1993) refs #1850 Co-authored-by: Can Celik <ogulcancelik@gmail.com> chore: approve merged contributor patrick-xin docs: add simplified chinese readme (#1990) * docs: add simplified chinese readme * fix: publish simplified chinese readme * fix: require localized readme for releases * docs: publish simplified chinese readme --------- Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix: restore python3 commit hook fix: select windows python command in commit hook (#1994) chore: approve merged contributor bioform fix: count ptmx master fds under devpts layouts (#1984) live_server_holds_one_pty_master_fd_per_pane counted only /proc/<pid>/fd links resolving to exactly /dev/ptmx. On devpts setups such as WSL2 and many containers, a PTY master fd resolves to /dev/pts/ptmx instead, so the test found zero masters and failed with "had 0 ptmx master fds; expected 1" even though the server held the pane's master fd. Match both ptmx node paths; pty slaves still resolve to /dev/pts/<N> and stay uncounted, so the one-master-per-pane invariant is unchanged. perf: skip rendering hidden pty updates fix: preserve linked worktree workspace labels perf: replace agent spinners with static status marks fix: ignore nested codex session reports (#1927) * fix: ignore nested codex session reports refs #1789 * test: cover matching codex thread id --------- Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix: resolve relative plugin commands from plugin root refs #1949 docs: update sponsor acknowledgements fix: keep focus on the current workspace when another self-closes (#1912) handle_pane_died only clamped the stale active/selected indices after removing a workspace that closed itself, so it never shifted them down when the removed workspace sat before the focused one. With workspaces [x, y, z] and focus on y, removing x left active pointing at z, silently moving the user into a workspace they did not select. Resolve both indices by workspace id across the removal, as close_selected_workspace already does, keeping the clamp as the fallback for when the removed workspace was the focused one. Also scroll the focused workspace back into view, which the explicit-close path already did and this one did not. refs #1621 Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix: make native windows checks reliable (#1925) chore: approve merged contributor season179 fix: close workspace when api closes its last tab (#1899) herdr tab close on a workspace's only tab returned tab_close_failed, while the TUI close-tab action on the same tab closes the tab and cascades to closing the workspace. Cascade in the API too, emitting tab.closed then workspace.closed. When the cascade would close a whole worktree group, return confirmation_required instead, matching pane.close and the TUI prompt. refs #1760 chore: approve merged contributor Pimpmuckl fix: preserve shift-enter in windows panes (#1909) refs #1743 fix: delay agent prompt submission refs #1878 fix: ignore empty clipboard writes refs #1893 fix: restrict unapproved pull requests to bug fixes ci: harden pull request workflow execution chore: approve merged contributor olafkfreund fix(nix): include SKILL.md in the package fileset (#1890) refs #1889 fix: repaint after transient terminal resizes fix: preserve prefix mode for modifier events refs #1870 feat: print bundled agent skill and hint next cli steps chore: approve merged contributor yianL fix: preserve focus when closing a non-focused workspace (#1877) refs #1328 fix: support non-us shifted keybindings (#1876) * fix: support non-us shifted keybindings refs #1870 * docs: require current pull request base fix: improve sidebar worktree hierarchy and reordering (#1873) * fix: clarify sidebar worktree hierarchy refs #1694 * feat: reorder worktree groups as a unit refs #1694 fix: avoid renders for passive mouse motion (#1872) refs #1865 perf: avoid full pane renders for spinner animation (#1868) ci: gate pull request review by scope fix(nix): add rust-analyzer to development shell chore: consolidate project skills docs: define maintainer and agent contribution workflows feat: switch IME to ASCII in prefix mode on Windows (Korean IME) (#1823) * 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> perf: eliminate repeated workspace git discovery (#1842) * perf: eliminate repeated workspace git discovery refs #1838 * fix: keep workspace labels current without extra redraws refs #1838 * fix: retain discovered workspace git metadata refs #1838 fix: copy retained mouse selections with keyboard shortcuts refs #1782 fix: run pi worktree ext on background chore: approve merged contributor ogulcancelik fix: bundle modern ConPTY on Windows (#1828) refs #1533 refs #1644 fix: harden hermes lifecycle detection feat: filter keybind help (#1832) * feat: filter keybind help refs #1825 * fix: recognize shifted slash in keybind help --------- Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> feat: add grok cli integration for native session restore (#1807) * feat: add grok cli integration for native session restore tl;dr: adds `herdr integration install grok` (session-identity tier: a SessionStart hook reports the grok session id for native restore; state stays screen-detected via the bundled manifest). Grok merges every ~/.grok/hooks/*.json, so install writes a self-contained hooks/herdr.json registering a SessionStart hook next to hooks/herdr-agent-state.sh, and never touches other hook files. The hook prefers $GROK_SESSION_ID (injected into every grok hook process) and reports through the socket API; herdr:grok is a reserved-native-state source so screen detection stays authoritative. Restore uses `grok --resume <id>`. Additive JSON API change; no protocol bump; the schema artifact is regenerated. refs #1800 * fix: honor GROK_HOME and validate grok hook config in status Review follow-ups: grok_dir now resolves GROK_CONFIG_DIR (herdr test seam) before GROK_HOME (the grok CLI's own config-home override) before ~/.grok, so hook installs land where grok actually looks. Integration status now also validates the herdr-owned hooks/herdr.json next to the hook script — missing, corrupt, or no-longer-referencing configs report the install as outdated instead of masking a hook grok never invokes. refs #1800 * fix: validate the exact grok session hook command in status The config validator matched on a filename substring, so a SessionStart command like `echo herdr-agent-state.sh`, or one invoking the script without the `session` action, still reported the integration as current while grok never delivered a session id. Status now compares against the exact command the installer writes, via a shared builder, and the degradation tests cover both nonfunctional shapes. refs #1800 * fix: require a command-type grok session hook in status validation A config entry carrying the exact generated command under a non-command hook type (e.g. "http") is never executed by grok, so it must not report the integration as current. The validator now also requires type = "command", with a degradation test for the mismatched-type shape. refs #1800 * fix: validate the complete grok hook config refs #1800 --------- Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> chore: approve contributor corrius chore: approve contributor adamkrellenstein fix: report truncated pane reads refs #1717 fix: make known-agent exit process-owned refs #1792 refs #1648 fix: answer XTWINOPS size queries in panes refs #835 chore: approve contributor joonhwan fix: prefer branded homepage search image fix: consume handled url click gestures refs #1761 fix: preserve kitty graphics during host repaints refs #1628 fix: inherit host palette in pane queries (#1759) refs #1752 Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> chore: approve contributor carlesso chore: approve contributor badlogic fix: preserve kitty printable key releases refs #1746 fix: preserve physical escape on windows refs #1736 fix: respect OMP config directory override refs #1696 chore: relicense herdr under apache-2.0 refactor: centralize new terminal cwd selection replaces contributions from Pitchfork-and-Torch refactor: name darwin nix toolchain inputs replaces contributions from DeevsDeevs refactor: simplify macos input source ownership replaces contributions from sf-jin-ku refactor: clarify opencode event state mapping replaces contributions from soar refactor: simplify cursor session reporting replaces contributions from udirom refactor: simplify kimi lifecycle reporting replaces contributions from wbxl2000 refactor: simplify qoder session reporting replaces contributions from wayneleelwc refactor: replace pane mouse and clipboard internals replaces contributions from othavi0 feat(docs): publish versioned release documentation fix: keep reading while pty input is blocked refs #1295 fix: fall back to sh for remote path discovery refs #1201 docs: add alternate-screen output fallback docs: update preview manifest fix(website): add agent automation to docs sidebar docs: update website manifest for v0.7.5 release: v0.7.5 docs: finalize release docs and refresh agent skill docs: document alternate-screen automation limits fix: link plugins without a running server refs #1670 fix: reject oversized text pastes safely (#1676) refs #1665 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> fix: bound linux sound player lifetime (#1677) refs #1622 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix: discover canonical mise remote installs refs #1201 fix: use pi settled lifecycle event docs: explain Windows cursor and IME trade-off refs #1637 fix: make plugin state global across sessions refs #1174 fix: show agent targets before options in help fix: bound stalled agent prompt waits fix: report kimi questions as blocked fix: avoid rechecking cached pane cwd (#1616) refs #1603 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> feat: add plugin-driven agent views and startup hooks feat: make agent automation a first-class cli primitive fix: preserve plugin command arguments (#1613) fix: show current workspace name in close confirmation refs #1364 feat: connected tree glyphs, workspace grouping, and search auto-select in navigator refs #1611 perf: cache windows foreground process snapshots refs #1158 fix: preserve terminal live follow through resize fix: support javascript integrations on windows refs #1531 fix: serialize opencode lifecycle reports refs #1519 fix: detect codex working with static titles (#1574) * fix: detect codex working with static titles refs #1563 * fix: ignore interrupted codex footer refs #1563 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> fix: report unknown config keys (#1575) * fix: report unknown config keys refs #1573 * fix: preserve mixed config warning context refs #1573 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> perf: batch contiguous terminal diff writes refs #283 fix: preserve lone escape input boundaries refs #541 feat: add optional workspace name prompt fix: update windows conpty smoke for pane-first agents feat: harden named agent cli workflows refs #1525 docs: update preview manifest test(windows): support headless console runners fix(windows): prevent remaining subprocess console flashes refs #1468 perf: adopt libghostty integration improvements refs #276 ci: gate Greptile reviews by contributor approval fix: detect grok background work from pinned status fix: detect grok state from terminal signals chore: update vendored libghostty-vt refs #276 fix: follow foreground process group leader cwd refs #1472 refactor: extract active tab surface rendering fix: preserve selection when auto-copy is disabled (#1496) refs #1471 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> fix(windows): suppress noninteractive process windows refs #1468 docs: update preview manifest fix(windows): hide agent manifest curl windows refs #1468 fix: stop agent wait when pane closes (#1488) chore: enable zed lsp feat: configure collapsed sidebar startup state refs #1463 feat: add configurable sidebar token styles feat(macos): support agent environment hints refs https://github.com/ogulcancelik/herdr/discussions/679 fix: preserve plugins during live handoff refs #893 fix: handle pane graphics stream disconnect race fix: guard cli requests against protocol mismatches (#1453) refs #1435 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Can Celik <ogulcancelik@gmail.com> test: wait for multi-client resize propagation docs: update preview manifest docs: update website manifest for v0.7.4 release: v0.7.4 docs: finalize next release documentation docs: add popup pane usage examples refs #1125 test: shorten pane graphics socket fixture paths feat: add floating popup panes refs #1125 feat: add isolated pane graphics streaming fix: preserve escape before sgr mouse reports refs #1334 feat(website): homepage plugin showcase, live star count, faithful claude code mock fix(linux): scope foreground process discovery to panes refs #1399 fix: reassemble split sgr mouse input refs #1382 fix: preserve associated emoji input (#1410) refs #1404 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> test: shorten macos api socket fixture path ci: restore fast reliable checks fix: flush live handoff responses before exit feat: configure sidebar entry gaps fix: stop copy mode line-end at text (#1411) fix: forward horizontal wheel events to panes (#1402) refs #1349 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> fix: retry dropped omp lifecycle reports refs #1310 fix(website): scope docs search by locale and channel fix: forward outer focus events to panes (#1388) * fix: forward outer focus events to panes refs #1337 * fix: preserve ordered focus forwarding refs #1337 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> fix: show renamed single tabs in agents sidebar refs #1369 fix: reap detached custom command children (#1384) * fix: reap detached custom command children refs #1360 * test: harden child cleanup and sidebar metrics refs #1360 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> docs: explain modified-arrow shell bindings refs #1370 fix: keep Claude working during /btw refs #1366 docs: streamline configuration guide fix: allow slow server shutdowns fix: detect reordered codex title spinners refs #1281 feat: add configurable sidebar metadata tokens docs: add versioned config reference refactor: centralize agent identity lookup docs: add troubleshooting guide refs #1116 fix: number collapsed agent rows by list position refs #1344 fix: keep windows api connections open refs #1279 fix: match homepage mock pane chrome and sidebar to tui rendering fix: clarify config fallback diagnostics fix: detach windows server from host terminal refs #1329 chore: remove unmerged approved contributors feat: add copy-on-select setting feat: add maki agent support (#1302) * feat: add maki detection support Maki always draws a one-line status bar at the bottom of the screen, so the manifest reads just that row: a braille spinner before the `[BUILD]`/`[PLAN]`/`[BASH]` mode label means working, the bare label means idle. Blocked states come from the panels that replace the input box, the `Permission Required` dialog with its `y Allow`/`n Deny` hint rows and the `Plan complete` form. Maki emits no OSC title or progress, so these screen rules are the only evidence. Also wires `maki` through process-name identification, per-agent sound overrides, and the website manifest catalog. * docs: stage maki detection docs chore: approve contributor tontinton docs: refresh herdr agent skill refs #1297 fix: improve config diagnostic delivery fix: stop windows installer from rewriting config refs #1162 fix: follow foreground cwd for new terminals refs #1245 feat: add copy mode search and tmux word motions refs #1230 docs: document windows config path ci: pin rust toolchain to 1.96.1 fix: preserve explicit session sockets during live handoff refs #1180 reported-by: dvic based-on: https://github.com/dvic/herdr/pull/3 fix: use drawn cursor by default in wsl refs #930 fix: reanchor pi status after session replacement (#1189) * fix: reanchor pi status after session replacement refs #943 * fix: guard active pi sessions from nested startup refs #943 * fix: serialize pi session replacement reports refs #943 fix: detect active amp turns refs #1208 chore: approve merged contributor kovalov fix: refresh tab bar after plugin-driven tab rename (#1179) refs #1111 Co-authored-by: Vlad Kovalov <kovalov@users.noreply.github.com> fix: make remote helper installs shell-portable refs #1203 fix: match shifted indexed number keybinds refs #1184 fix: align collapsed sidebar agent rows (#1182) refs #1168 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> docs: link plugin marketplace from readme docs: lean readme, add terminal trove gold sponsor docs: enforce localized release doc parity refs #1151 docs: update website manifest for v0.7.3 release: v0.7.3 docs: stage 0.7.3 release notes fix: restore workspace picker keyboard navigation refs #1149 fix: avoid render-time plain url scans docs: change sponsorships tiers and add lead sponsor docs: update preview manifest ci: ignore microsoft apt repos in artifact builds ci: clear zig cache before windows checks fix: keep cursor hide inside sync output refs #967 fix: guard windows process tree cycles refs #1083 fix: clear done status on repeated pane focus fix: preserve navigator search commands (#1140) Navigator search treated Escape as either modal close or query reset, which left no way to return to command mode while keeping the filtered row set. `/` also rebuilt search mode by clearing the query, so a user could not resume editing the same filter after backing out. Make Escape leave search focus only, reserve command-mode Escape for closing the picker, and let `/` refocus the existing query. The footer now lists the shortcuts for the active mode so command keys are not advertised while text input owns them. refs #1115 docs: update website manifest for v0.7.2 release: v0.7.2 docs: finalize release docs fix: send shifted punctuation as text in kitty mode refs #1105 fix: preserve alt-shift letters (#1102) refs #1088 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> perf: move session autosaves off main loop fix: stop windows server over named pipes refs #1113 fix: support esc interrupt for github copilot (#1120) * fix: support esc interrupt for github copilot Github Copilot CLI version v1.0.69-2 introduces the "esc interrupt" prompt. Update the Github Copilot agent detection manifest to match this prompt and correctly report the agent as working. Add test cases in tests.rs to verify state detection. Synchronize the website's copy of the manifest. refs #1119 * test: clean up github copilot detection rules and tests Clean up formatting and comments for the github copilot detection rule and tests. refs #1119 * test: remove copilot manifest literal test --------- Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> chore: approve contributor liby docs: remove star history embed fix: honor prefix in copy mode refs #681 Based on PR #1092 by @reobin. Co-authored-by: Robin Gagnon <me@reobin.dev> fix: preserve wrapped pane links (#1099) refs #1098 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Can Celik <ogulcancelik@gmail.com> feat: expose pane scroll state docs: update sponsors fix: bridge remote image file drops (#1085) refs #828 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> docs: add community video section test: synchronize multi-client resize assertion fix: clarify remote ssh auth failures (#1084) refs #1034 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> fix: preserve shifted punctuation for alt legacy input (#1076) refs #1066 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> docs: expose stats page in website nav fix: write pane copies to native windows clipboard (#1082) refs #1067 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> chore: approve merged contributor akbash-bot fix: focus parent after worktree removal (#1075) refs #1004 Co-authored-by: akbash-bot <akbash-bot@users.noreply.github.com> docs: update preview manifest ci: use homebrew zig for preview macos builds feat(website): add japanese and simplified chinese docs with i18n, translation issue flow fix: detect grok build working and blocked states from current ui chrome (#1055) * fix: detect grok build working and blocked states from current ui chrome refs #1017 * fix: sync grok detection website manifest refs #1017 --------- Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> feat: add public stats page at herdr.dev/stats ci: accept common environment aliases in issue gate refactor(nix): replace apple-sdk wrappers with xcbuild (#995) Co-authored-by: Can Celik <ogulcancelik@gmail.com> fix: retry pi state socket reports refs #1049 fix: support custom command keybindings on windows refs #1041 fix: switch prefix ASCII input source on the foreground client (#1016) * fix: switch prefix ASCII input source on the foreground client The macOS prefix ASCII-switch ran in the headless server, whose TISCopyCurrentKeyboardInputSource read is a stale per-process cache (refreshed only via a main-run-loop notification the server never pumps), so prefix could leave the terminal on the wrong input source. Forward it to the foreground client, like clipboard: sync_prefix_input_source emits AppEvent::PrefixInputSource as an intent; the server forwards it to the foreground client and the monolithic app applies it in-process via RealPrefixInputSource, which pumps its main run loop before the stale TIS read. Key it on Mode::wants_ascii_input so multi-level prefix commands keep ASCII until returning to the terminal, and restore the IME for rename text entry. Adds ServerMessage::PrefixInputSource to the already-unreleased protocol 15. refs #774 * fix: keep prefix input-source switch out of the headless server process An App-internal drain (the exhaustive drain at the top of handle_api_request, reachable from prefix-key runtime mutations) can consume a queued PrefixInputSource intent before the server's forwarding drain sees it, applying the TIS switch in the headless server process and stranding the restore state. Gate the in-process switch on App.local_input_source_switch, disabled by the headless server alongside the other local side effects, so a swallowed intent degrades to a skipped switch. Widen the forwarding-bypass maintenance test to the handle_internal_event_with_prefix_sync wrapper. refs #774 * docs: clarify prefix input-source comments and flag docs refs #774 * fix: bump protocol for prefix input source refs #774 --------- Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com> test: wait for client frame before crash assertion test: keep windows smoke cleanup non-fatal test: wait for windows smoke pane output fix: wait for server stop socket cleanup chore: approve contributor TonyxSun refactor: route headless deferred creates through runtime helpers refactor: route cli pane placement through runtime adapters refactor: route cli workspace tab worktree through runtime adapters refactor: route tui runtime mutations through typed adapters feat: expose session snapshot through cli feat: emit layout update events for runtime layout changes chore: approve contributor ppggff refactor: route worktree tui actions through runtime wrappers refactor: route pane layout tui actions through runtime wrappers fix: allow foreground agent session takeover refs #943 chore: approve merged contributor DevSrSouza fix: kitty graphics streams freeze on partial updates and leak host images (#948) * fix: request a render on kitty graphics writes pty output containing a kitty graphics sequence only scheduled the deferred settle render. that render's wake-up notifies the render loop only when the dirty flag was not already set, so under a continuous stream of image updates the notify can be dropped and the pane keeps showing an old frame until something else forces a full render. request an immediate render like any other output. the dirty flag and the minimum render interval still coalesce chunked writes, and the settle render stays as a backstop. refs #947 * fix: fingerprint kitty images exactly across retransmissions the image fingerprint hashed only the first, middle, and last 4096 bytes of pixel data. a program that streams updates by retransmitting one image id keeps the length and dimensions constant, so a frame whose changed pixels all fall outside those three windows gets the same fingerprint as the frame before it. the encoder then treats the image as already uploaded, emits no graphics bytes, and the frame is dropped as identical to the previous render. the pane stays stuck on the last uploaded image until a resize resets the graphics surface. hash the full payload instead. to keep that off the render hot path, cache the fingerprint per image id and recompute it only when the image's transmit time changes. libghostty-vt already refreshes the transmit time on every transmission; vendor patch 0002 exposes it through a new GHOSTTY_KITTY_IMAGE_DATA_TRANSMIT_TIME_NS accessor. a static image costs one map lookup per render and a streaming image one hash per transmitted frame. refs #947 * fix: delete superseded host kitty images host image ids derive from image content, so a pane image whose pixels change maps to a new host id on every u…
1 parent 56db621 commit 2854d5d

5 files changed

Lines changed: 102 additions & 5 deletions

File tree

‎src/app/input/modal.rs‎

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,7 @@ pub(super) fn apply_context_menu_action(
935935
pane_id,
936936
..
937937
},
938-
Some("Split stacked"),
938+
Some("Stack"),
939939
) => {
940940
state.selected = ws_idx;
941941
state.active = Some(ws_idx);
@@ -1376,6 +1376,16 @@ impl App {
13761376
self.split_focused_pane_via_api(crate::api::schema::SplitDirection::Down);
13771377
self.state.mode = Mode::Terminal;
13781378
}
1379+
(
1380+
ContextMenuKind::Pane {
1381+
ws_idx, pane_id, ..
1382+
},
1383+
Some("Stack"),
1384+
) => {
1385+
self.focus_pane_internal_via_api(ws_idx, pane_id);
1386+
self.split_focused_pane_via_api(crate::api::schema::SplitDirection::Stacked);
1387+
self.state.mode = Mode::Terminal;
1388+
}
13791389
(
13801390
ContextMenuKind::Pane {
13811391
ws_idx, pane_id, ..
@@ -2246,6 +2256,50 @@ mod tests {
22462256
assert_eq!(state.mode, Mode::Navigate);
22472257
}
22482258

2259+
/// Every production activation of a pane menu item — click and Enter alike —
2260+
/// funnels through `apply_context_menu_action_via_api`, which had no `"Stack"`
2261+
/// arm, so the item fell through to `leave_modal` and did nothing at all.
2262+
/// `handle_context_menu_key` above is `#[cfg(test)]`, so the pre-existing
2263+
/// `keyboard_context_menu_split_stacked_makes_a_stack` asserted against a
2264+
/// test-only mirror of dispatch and stayed green while the real binary was
2265+
/// broken. Any new menu item needs a test on *this* path to mean anything.
2266+
#[tokio::test]
2267+
async fn clicking_context_menu_stack_makes_a_stack() {
2268+
let mut app = app_with_test_workspaces(&["main"]);
2269+
app.state.default_shell = "/usr/bin/true".into();
2270+
app.state.active = Some(0);
2271+
let pane_id = app.state.workspaces[0].tabs[0].root_pane;
2272+
let menu = ContextMenuState {
2273+
kind: ContextMenuKind::Pane {
2274+
ws_idx: 0,
2275+
tab_idx: 0,
2276+
pane_id,
2277+
source_pane_id: None,
2278+
has_manual_label: false,
2279+
right_click_passthrough: false,
2280+
},
2281+
x: 0,
2282+
y: 0,
2283+
list: MenuListState::new(0),
2284+
};
2285+
let idx = menu
2286+
.items()
2287+
.iter()
2288+
.position(|item| *item == "Stack")
2289+
.expect("the pane menu offers stacking");
2290+
2291+
app.apply_context_menu_action_via_api(menu, idx);
2292+
2293+
let panes = app.state.workspaces[0].tabs[0]
2294+
.layout
2295+
.panes(ratatui::layout::Rect::new(0, 0, 100, 40));
2296+
assert_eq!(panes.len(), 2, "clicking Stack must add a second pane");
2297+
assert!(
2298+
panes.iter().all(|info| info.stack.is_some()),
2299+
"both panes must land in one stack, not a split"
2300+
);
2301+
}
2302+
22492303
#[test]
22502304
fn context_menu_toggles_pane_right_click_passthrough() {
22512305
let mut app = app_with_test_workspaces(&["main"]);

‎src/app/input/mouse.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3756,8 +3756,8 @@ mod tests {
37563756
let stacked_index = menu
37573757
.items()
37583758
.iter()
3759-
.position(|item| *item == "Split stacked")
3760-
.expect("the pane menu offers a stacked split");
3759+
.position(|item| *item == "Stack")
3760+
.expect("the pane menu offers stacking");
37613761
menu.list = MenuListState::new(stacked_index);
37623762

37633763
app.state.context_menu = Some(menu);

‎src/app/state.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1241,7 +1241,7 @@ impl ContextMenuState {
12411241
if source_pane_id.is_some() {
12421242
items.push("Swap with focused pane");
12431243
}
1244-
items.extend(["Split right", "Split down", "Split stacked", "Zoom"]);
1244+
items.extend(["Split right", "Split down", "Stack", "Zoom"]);
12451245
items.push(if right_click_passthrough {
12461246
"Use Herdr right-click menu"
12471247
} else {

‎src/ui/keybind_help.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ pub(super) fn keybind_help_groups(app: &AppState) -> Vec<HelpGroup> {
141141
let panes = vec![
142142
help_entry(keybind_label(&kb.split_vertical), "split vertical"),
143143
help_entry(keybind_label(&kb.split_horizontal), "split horizontal"),
144-
help_entry(keybind_label(&kb.split_stacked), "split stacked"),
144+
help_entry(keybind_label(&kb.split_stacked), "stack pane"),
145145
help_entry(keybind_label(&kb.close_pane), "close pane"),
146146
help_entry(keybind_label(&kb.rename_pane), "rename pane"),
147147
help_entry(keybind_label(&kb.edit_scrollback), "edit scrollback"),

‎src/ui/menus.rs‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ pub(super) fn render_navigate_overlay(app: &AppState, frame: &mut Frame, area: R
142142
let new_tab = prefix_rhs_label(&kb.new_tab);
143143
let split_vertical = prefix_rhs_label(&kb.split_vertical);
144144
let split_horizontal = prefix_rhs_label(&kb.split_horizontal);
145+
let split_stacked = prefix_rhs_label(&kb.split_stacked);
145146
let close_pane = prefix_rhs_label(&kb.close_pane);
146147
let zoom = prefix_rhs_label(&kb.zoom);
147148
let resize = prefix_rhs_label(&kb.resize_mode);
@@ -171,6 +172,8 @@ pub(super) fn render_navigate_overlay(app: &AppState, frame: &mut Frame, area: R
171172
Span::styled(" split│ ", dim),
172173
Span::styled(split_horizontal, key),
173174
Span::styled(" split─ ", dim),
175+
Span::styled(split_stacked, key),
176+
Span::styled(" stack ", dim),
174177
Span::styled(close_pane, key),
175178
Span::styled(" close ", dim),
176179
Span::styled(zoom, key),
@@ -313,3 +316,43 @@ pub(super) fn render_context_menu(app: &AppState, frame: &mut Frame) {
313316
let mut state = ListState::default().with_selected(Some(menu.list.highlighted));
314317
frame.render_stateful_widget(list, inner, &mut state);
315318
}
319+
320+
#[cfg(test)]
321+
mod tests {
322+
use super::*;
323+
use ratatui::backend::TestBackend;
324+
use ratatui::Terminal;
325+
326+
#[test]
327+
fn the_navigate_overlay_offers_all_three_ways_to_add_a_pane() {
328+
let app = AppState::test_new();
329+
// Wide enough that the bar cannot truncate; a narrow terminal drops the
330+
// tail entries and would hide the entry under test for the wrong reason.
331+
let area = Rect::new(0, 0, 200, 3);
332+
let mut terminal = Terminal::new(TestBackend::new(area.width, area.height)).unwrap();
333+
terminal
334+
.draw(|frame| render_navigate_overlay(&app, frame, area))
335+
.unwrap();
336+
337+
let rendered: String = terminal
338+
.backend()
339+
.buffer()
340+
.content()
341+
.iter()
342+
.map(|cell| cell.symbol())
343+
.collect();
344+
345+
for label in ["split│", "split─", "stack"] {
346+
assert!(
347+
rendered.contains(label),
348+
"the navigate overlay should offer {label}: {rendered:?}"
349+
);
350+
// The key precedes the label, so a dropped default binding renders as
351+
// "unset stack" — present in the bar but useless.
352+
assert!(
353+
!rendered.contains(&format!("unset {label}")),
354+
"{label} should show its default binding: {rendered:?}"
355+
);
356+
}
357+
}
358+
}

0 commit comments

Comments
 (0)