Synced from monorepo
Changes: - Detect the herdr multiplexer - Mark /gboom as non-production code - Bound peak memory when loading a large session - Add a subagent lifecycle soak bounding threads, fds, and heap - Stream inherited replay to bound fork memory - Copy full plan from plan approval with y - Stop armed signature verification from deleting the managed-deny smoke policy - Add source-tagged terminal version telemetry - Show the UI instantly and fetch models and settings in the background - Session test helpers - computer_reason on the ConversationHistoryDone trailer
This commit is contained in:
parent
47348d13ec
commit
b41c75a578
92 changed files with 9410 additions and 3788 deletions
|
|
@ -553,6 +553,8 @@ pub struct PlanViewerExtras {
|
|||
pub comment_hovered: bool,
|
||||
pub abandon_button_area: Option<Rect>,
|
||||
pub abandon_hovered: bool,
|
||||
pub copy_button_area: Option<Rect>,
|
||||
pub copy_hovered: bool,
|
||||
pub last_click_at: Option<std::time::Instant>,
|
||||
pub gutter_drag_start: Option<usize>,
|
||||
pub gutter_drag_end: Option<usize>,
|
||||
|
|
@ -822,8 +824,7 @@ impl LineViewerState {
|
|||
}
|
||||
|
||||
/// Whether the plan modal should render the action-button footer.
|
||||
/// True for both modes: plan-approval (q/c/s|a) and casual
|
||||
/// (c/s — quit via the close-X button instead of a footer button).
|
||||
/// True for plan-approval and casual plan preview (not plain file preview).
|
||||
pub fn show_footer(&self) -> bool {
|
||||
self.plan
|
||||
.as_ref()
|
||||
|
|
@ -1477,10 +1478,7 @@ pub fn render_line_viewer(
|
|||
// Buttons use the same `key bold + label dim` treatment as
|
||||
// `render_modal_shortcuts`, sit in a single row separated by
|
||||
// ` | `, centered within the modal frame.
|
||||
//
|
||||
// - Plan-approval: q quit | c comment | s send / a approve
|
||||
// - Casual preview: c comment | s send (no `q` —
|
||||
// the close-X button handles closing in casual mode)
|
||||
// Casual preview omits `q` (close via the X button).
|
||||
if viewer.show_footer() && inner.height >= 2 {
|
||||
let div_y = inner.y + inner.height - 2;
|
||||
let div_style = Style::default().fg(theme.gray_dim).bg(theme.bg_base);
|
||||
|
|
@ -1492,11 +1490,15 @@ pub fn render_line_viewer(
|
|||
let abandon_hovered = viewer.plan_ref().is_some_and(|p| p.abandon_hovered);
|
||||
let comment_hovered = viewer.plan_ref().is_some_and(|p| p.comment_hovered);
|
||||
let approve_hovered = viewer.plan_ref().is_some_and(|p| p.approve_hovered);
|
||||
let copy_hovered = viewer.plan_ref().is_some_and(|p| p.copy_hovered);
|
||||
let is_approval = viewer.feedback_active();
|
||||
|
||||
let comment_spans = build_shortcut_button('c', "comment", comment_hovered, theme);
|
||||
let comment_w: u16 = comment_spans.iter().map(|s| s.width() as u16).sum();
|
||||
|
||||
let copy_spans = build_shortcut_button('y', "copy plan", copy_hovered, theme);
|
||||
let copy_w: u16 = copy_spans.iter().map(|s| s.width() as u16).sum();
|
||||
|
||||
// In approval mode, always show `a approve`. When there are
|
||||
// pending review comments, also show `s revise` (request changes).
|
||||
// In approval mode, show `a approve` (or `a approve w/ comments`
|
||||
|
|
@ -1555,18 +1557,20 @@ pub fn render_line_viewer(
|
|||
let sep_w: u16 = 5; // separator is fixed-width ASCII; matches modal_window.rs:565
|
||||
let sep_style = Style::default().fg(theme.gray_dim).bg(theme.bg_base);
|
||||
|
||||
// Total width: [action] + (sep + revise)? + sep + comment[badge?] + (sep + quit)?
|
||||
let mut total_w: u16 = 0;
|
||||
let mut base_w: u16 = 0;
|
||||
if action_w > 0 {
|
||||
total_w = total_w.saturating_add(action_w).saturating_add(sep_w);
|
||||
base_w = base_w.saturating_add(action_w).saturating_add(sep_w);
|
||||
}
|
||||
if revise_w > 0 {
|
||||
total_w = total_w.saturating_add(revise_w).saturating_add(sep_w);
|
||||
base_w = base_w.saturating_add(revise_w).saturating_add(sep_w);
|
||||
}
|
||||
total_w = total_w.saturating_add(comment_w).saturating_add(badge_w);
|
||||
base_w = base_w.saturating_add(comment_w).saturating_add(badge_w);
|
||||
if let Some((_, w)) = &quit_spans {
|
||||
total_w = total_w.saturating_add(sep_w).saturating_add(*w);
|
||||
base_w = base_w.saturating_add(sep_w).saturating_add(*w);
|
||||
}
|
||||
let with_copy_w = base_w.saturating_add(sep_w).saturating_add(copy_w);
|
||||
let show_copy = with_copy_w <= inner.width;
|
||||
let total_w = if show_copy { with_copy_w } else { base_w };
|
||||
|
||||
if total_w <= inner.width {
|
||||
let mut x = inner.x + (inner.width - total_w) / 2;
|
||||
|
|
@ -1620,6 +1624,20 @@ pub fn render_line_viewer(
|
|||
x += badge_w;
|
||||
}
|
||||
|
||||
if show_copy {
|
||||
buf.set_string(x, bottom_y, separator, sep_style);
|
||||
x += sep_w;
|
||||
let copy_x = x;
|
||||
for span in ©_spans {
|
||||
let w = span.width() as u16;
|
||||
buf.set_span(x, bottom_y, span, w);
|
||||
x += w;
|
||||
}
|
||||
viewer.plan_mut().copy_button_area = Some(Rect::new(copy_x, bottom_y, copy_w, 1));
|
||||
} else {
|
||||
viewer.plan_mut().copy_button_area = None;
|
||||
}
|
||||
|
||||
// Quit button — approval mode only.
|
||||
if let Some((spans, w)) = quit_spans {
|
||||
buf.set_string(x, bottom_y, separator, sep_style);
|
||||
|
|
@ -1640,6 +1658,7 @@ pub fn render_line_viewer(
|
|||
let plan = viewer.plan_mut();
|
||||
plan.approve_button_area = None;
|
||||
plan.comment_button_area = None;
|
||||
plan.copy_button_area = None;
|
||||
plan.abandon_button_area = None;
|
||||
}
|
||||
}
|
||||
|
|
@ -1683,6 +1702,20 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plan_preview_exposes_full_raw_markdown_for_copy() {
|
||||
let body = "# Plan\n\n- Do the thing\n- Then ship";
|
||||
let mut viewer = LineViewerState::open_markdown_content("plan.md", body.to_owned(), None)
|
||||
.expect("markdown content should open");
|
||||
viewer.kind = LineViewerKind::PlanPreview;
|
||||
viewer.prepare_layout(80, 20);
|
||||
|
||||
assert_eq!(
|
||||
viewer.markdown_content_for_feedback().as_deref(),
|
||||
Some(body)
|
||||
);
|
||||
}
|
||||
|
||||
fn line_text(line: &Line<'_>) -> String {
|
||||
line.spans
|
||||
.iter()
|
||||
|
|
|
|||
|
|
@ -1826,6 +1826,8 @@ impl PromptWidget {
|
|||
terminal.multiplexer = %evt.terminal.multiplexer,
|
||||
terminal.is_ssh = evt.terminal.is_ssh,
|
||||
terminal.term_var = %evt.terminal.term_var,
|
||||
terminal.term_version = %evt.terminal.term_version,
|
||||
terminal.term_version_source = %evt.terminal.term_version_source,
|
||||
key.code = %evt.key_code,
|
||||
key.modifiers = %evt.key_modifiers,
|
||||
key.kind = %evt.key_kind,
|
||||
|
|
|
|||
Loading…
Reference in a new issue