feat: v1.3 polish — agent activity, scrollback UX, graceful TOML, README#40
Merged
feat: v1.3 polish — agent activity, scrollback UX, graceful TOML, README#40
Conversation
Sidebar gains a per-worktree agent indicator: '!' (warn) when a shell
rang BEL — Claude Code, gh CLI, and most readline tools ring it on
prompt — and '…' (dim) when the shell wrote output in the last few
seconds. The bell flag clears when the user activates the worktree.
Aggregated across all terminals in a worktree; quiet shells stay silent.
Scrollback-mode UX:
- Mouse-wheel down to the live edge auto-exits scrollback (you're back
where the shell is writing, so we drop the mode for you).
- Any non-navigation keypress in scrollback (letters, digits, Enter,
Backspace, Ctrl+anything) snaps to the live edge and reaches the PTY,
matching tmux copy-mode UX.
Shell input:
- Shift+Enter now sends LF instead of the kitty 'CSI 13;2u' sequence;
most TUIs (Claude Code included) interpret LF as newline-in-input
while plain Enter (CR) submits.
Config robustness:
- Config::load_or_default_lossy: malformed TOML falls back to defaults
with a sidebar warning ('⚠ config.toml: parse error / using defaults;
see grove.log') instead of refusing to start. The full error goes to
the log for the user to read.
README:
- Rewritten example block with character-aligned columns.
- Documented every status badge (working tree + PR + CI + agent
activity), since the meaning was previously oral tradition.
- Documented the new v1.3 surfaces: activity footer, fetch cadence,
unified worktree modal, theme cycle, base-branch auto-detect.
- Cargo.toml bumped to 1.3.0.
Co-Authored-By: Claude <noreply@anthropic.com>
…stic)
Claude Code and most modern TUI agents announce their state via OSC 0
window-title updates: a braille spinner glyph (U+2800–U+28FF) prefixes
the title while output is streaming, then switches back to a static
'✳' once idle. This is a far more precise signal than "any PTY byte
in the last 2.5s" — no false positives from background log spam,
spinner ticks unrelated to agent work, or terminal redraws.
Implementation:
- TerminalActivity gains a `title` field, populated by a
`vt100::Callbacks` impl that pipes set_window_title and audible_bell
events into the shared activity slot.
- Parser switches from `vt100::Parser` to `vt100::Parser<GroveCallbacks>`
using `new_with_callbacks`. Bell-byte scanning in the reader thread
is removed — the callback handles it.
- `title_is_thinking()` classifies a title by its leading character
(U+2800–U+28FF braille block). Tested against real titles captured
from a Claude Code session ("⠂ Claude Code", "⠐ Run ls command…",
"✳ Say hi", "✳ Claude Code").
- `WorktreeTerminals::agent_state` uses the title as authoritative when
set, and falls back to the recent-output heuristic only for shells
that don't announce themselves.
Discovered via PTY capture of a real Claude Code session — see
/tmp/capture-osc.py + /tmp/drive-claude.py for the harness used.
Co-Authored-By: Claude <noreply@anthropic.com>
Without the kitty keyboard protocol, terminals send the same byte
(\\r) for both Enter and Shift+Enter — crossterm has nothing to
distinguish them, KeyEvent arrives as plain Enter, and our SHIFT
branch is never taken. That's why the previous fix only sent LF for
Ctrl+J (which terminals encode as raw 0x0A regardless of mode), not
for Shift+Enter.
init_terminal pushes DISAMBIGUATE_ESCAPE_CODES; restore_terminal pops
it. Both are best-effort: terminals that don't speak the protocol
silently ignore the escapes, so we tolerate the push/pop failing.
With this in place modern terminals (kitty, alacritty, foot, wezterm,
ghostty, recent iTerm2/Apple Terminal, the JetBrains terminals)
deliver Shift+Enter as KeyEvent { code: Enter, modifiers: SHIFT }, our
existing match arm sends LF, and Claude Code treats it as
newline-in-input.
Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A grab-bag of v1.3 polish driven by user feedback in this session:
!(warn) when a shell in that worktree rang the bell (BEL),…(dim) when it wrote output in the last ~2.5s. Aggregated across all terminals; bell clears on activate. Bell is the high-confidence "needs attention" signal that Claude Code,gh, and most readline tools already emit.\n) instead of the kittyCSI 13;2uescape, so it reaches Claude Code et al. as a literal newline.config.tomlno longer refuse to start grove. Falls back to defaults with a sidebar warning and logs the full error.Cargo.tomlbumped to 1.3.0.Test plan
cargo test(125 passed)cargo clippy --all-targets -- -D warningscleancargo fmtcleana— should snap to live edge and sendato the shell.config.tomland start grove — should see warning in sidebar and grove still launches with defaults.ghprompt) — sidebar row should show!.🤖 Generated with Claude Code