mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
feat(tui): add PR summary statusline items (#20892)
## Why? The Codex App already exposes branch and PR context in its branch-details UI. This brings the same context into the CLI footer as opt-in statusline items, so users can choose the extra signal without making the default footer busier. ## What? Add optional `pull-request-number` and `branch-changes` items to the configurable TUI status line. - `pull-request-number` shows the open PR for the current checkout and renders as a clickable terminal hyperlink when OSC 8 links are supported. - `branch-changes` shows committed additions/deletions against the repository default branch, or `No changes` when the branch has no committed diff. <img width="1257" height="261" alt="CleanShot 2026-05-03 at 20 44 15" src="https://github.com/user-attachments/assets/10b4380b-c3e9-4729-9ee1-3f742068fa47" /> ## Architecture This follows the same client/app-server split as the Codex App: the TUI owns presentation, caching, and optional rendering, while workspace-sensitive `git` and `gh` discovery runs through app-server. The new TUI-local `workspace_command` layer sends bounded, non-interactive `command/exec` requests to the active app-server. That makes the implementation remote-friendly: the TUI does not decide whether commands run in an embedded local workspace or a remote workspace, and it does not bypass app-server sandbox or permission policy. The branch summary logic stays internal to `codex-tui` because this PR only needs TUI statusline behavior. The command boundary is still isolated behind `WorkspaceCommandExecutor`, so the lookup code can be lifted or reused later without changing statusline rendering. ## How? - Add a TUI `WorkspaceCommandExecutor` abstraction backed by app-server `command/exec`. - Add branch summary probes for: - current branch name, - open PR metadata, - committed branch diff stats against the default branch. - Prefer remote-tracking default branch refs for diff stats, avoiding stale or absent local `main` branches. - Resolve PRs with `gh pr view` first, then fall back to commit-associated PR lookup across parent/fork repos. - Add `/statusline` picker entries, preview values, rendering, and OSC 8 clickable PR links. - Keep all probes best-effort so missing `git`, missing `gh`, auth failures, or non-git directories hide optional items instead of surfacing footer errors. ## Validation - `cargo test -p codex-tui branch_summary -- --nocapture` - Snapshot coverage for the `/statusline` preview/setup rendering paths - Hyperlink rendering coverage for clickable PR statusline cells
This commit is contained in:
1 parent
c2fed01550
commit
cc16995cc6
22 files changed
+1351
-10
No files matched your search
@@ -61,6 +61,24 @@ use crate::tui::FrameRequester;
|
||||
/// row boundary, which breaks normal terminal URL detection for long URLs that
|
||||
/// wrap across multiple rows.
|
||||
pub(crate) fn mark_url_hyperlink(buf: &mut Buffer, area: Rect, url: &str) {
|
||||
mark_hyperlink_cells(buf, area, url, |cell| {
|
||||
cell.fg == Color::Cyan && cell.modifier.contains(Modifier::UNDERLINED)
|
||||
});
|
||||
}
|
||||
|
||||
/// Marks any underlined buffer cells as an OSC 8 hyperlink.
|
||||
pub(crate) fn mark_underlined_hyperlink(buf: &mut Buffer, area: Rect, url: &str) {
|
||||
mark_hyperlink_cells(buf, area, url, |cell| {
|
||||
cell.modifier.contains(Modifier::UNDERLINED)
|
||||
});
|
||||
}
|
||||
|
||||
fn mark_hyperlink_cells(
|
||||
buf: &mut Buffer,
|
||||
area: Rect,
|
||||
url: &str,
|
||||
should_mark: impl Fn(&ratatui::buffer::Cell) -> bool,
|
||||
) {
|
||||
// Sanitize: strip any characters that could break out of the OSC 8
|
||||
// sequence (ESC or BEL) to prevent terminal escape injection from a
|
||||
// malformed or compromised upstream URL.
|
||||
@@ -75,8 +93,7 @@ pub(crate) fn mark_url_hyperlink(buf: &mut Buffer, area: Rect, url: &str) {
|
||||
for y in area.top()..area.bottom() {
|
||||
for x in area.left()..area.right() {
|
||||
let cell = &mut buf[(x, y)];
|
||||
// Only mark cells that carry the URL's distinctive style.
|
||||
if cell.fg != Color::Cyan || !cell.modifier.contains(Modifier::UNDERLINED) {
|
||||
if !should_mark(cell) {
|
||||
continue;
|
||||
}
|
||||
let sym = cell.symbol().to_string();
|
||||
|
||||
Reference in new issue
Block a user