Synced from monorepo
Synced from monorepo Changes: - Workspace server: report `/ready` as failed with dwell on hub connect failure - Refresh OIDC token for the Grok agent in the shell - ACP terminal output recorder - Cross-platform provider auth commands in the shell - Default `/resume` to Grok sessions with a hint for hidden external sessions - Resume sessions by title with `--resume` - Limit app-builder archive size - Data-driven tag labels for slash commands - Doctor fixes for tmux - Custom provider gateways and subprocess environment policy in the shell - `/tutorial` — opt-in onboarding tour of Grok Build - Soft and required CLI version checks in the shell - Privacy banner env overrides survive live settings updates - Add remote flag to override the image-edit model - Return profile fields from auth info even when the access token is expired - Add edit control on queued prompt rows - Keep fail-closed policy when clearing orphans with no team - Setting to disable the Ctrl+Space/F8 voice shortcut - Pass `--raw` to pw-record so Linux dictation works on older PipeWire - Validate git URLs when adding marketplace entries - Stop shipping stale tool-doc parameter and tool names - Re-point dashboard attach after `/fork` only when the parent was attached - Surface Grok Computer media-generation results as file-path chunks - Clear web background-task tray on kill and keep the task description - Show privacy upsell banner in agent view until acted on - Add tools-server client callback surface - Protect persistent global hook sources Source-Revision: 95d84f443eddcbed6cbfd6eed22e2eafe6b3939d
This commit is contained in:
parent
a5727c5960
commit
69f0ba880a
286 changed files with 22939 additions and 9624 deletions
|
|
@ -193,7 +193,12 @@ fn resume_body_rows(agent: &AgentView, width: u16) -> u16 {
|
|||
state,
|
||||
Some(current_repo.as_str()),
|
||||
);
|
||||
measure_entries(&picker_entries)
|
||||
// Reserve a row for the pinned hidden-external hint when shown.
|
||||
let hint_row = u16::from(
|
||||
!agent.app_chat_mode
|
||||
&& minimal_api::hidden_external_hint(entries.as_deref(), *source_filter).is_some(),
|
||||
);
|
||||
measure_entries(&picker_entries).saturating_add(hint_row)
|
||||
}
|
||||
|
||||
fn render_resume(
|
||||
|
|
@ -203,6 +208,7 @@ fn render_resume(
|
|||
theme: &Theme,
|
||||
) -> Option<(u16, u16)> {
|
||||
let cwd = agent.session.cwd.to_string_lossy().to_string();
|
||||
let chat_mode = agent.app_chat_mode;
|
||||
let Some(ActiveModal::SessionPicker {
|
||||
entries,
|
||||
state,
|
||||
|
|
@ -212,7 +218,7 @@ fn render_resume(
|
|||
else {
|
||||
return None;
|
||||
};
|
||||
let (title_row, search_row, divider_row, list_area, footer_row) = chrome_layout(area);
|
||||
let (title_row, search_row, divider_row, mut list_area, footer_row) = chrome_layout(area);
|
||||
|
||||
let entries_data = entries.as_deref().unwrap_or(&[]);
|
||||
let content_width = area.width.saturating_sub(2);
|
||||
|
|
@ -238,6 +244,9 @@ fn render_resume(
|
|||
state,
|
||||
Some(current_repo.as_str()),
|
||||
);
|
||||
let hidden_hint = (!chat_mode)
|
||||
.then(|| minimal_api::hidden_external_hint(entries.as_deref(), *source_filter))
|
||||
.flatten();
|
||||
|
||||
render_title(buf, title_row, theme, "Resume session");
|
||||
// Focus-aware search bar (cursor only when search is focused).
|
||||
|
|
@ -256,6 +265,21 @@ fn render_resume(
|
|||
);
|
||||
render_divider(buf, divider_row, theme);
|
||||
|
||||
// Pinned above the list so it stays visible regardless of list scroll.
|
||||
if let Some(hint) = hidden_hint.as_deref() {
|
||||
render_dim_line(
|
||||
buf,
|
||||
Rect {
|
||||
height: 1,
|
||||
..list_area
|
||||
},
|
||||
theme,
|
||||
hint,
|
||||
);
|
||||
list_area.y += 1;
|
||||
list_area.height = list_area.height.saturating_sub(1);
|
||||
}
|
||||
|
||||
let nsc = vec![false; picker_entries.len()];
|
||||
let hit = picker::render_picker_content(
|
||||
buf,
|
||||
|
|
@ -493,14 +517,22 @@ fn render_mcps(
|
|||
|
||||
// ─────────────────────────────── helpers ────────────────────────────────────
|
||||
|
||||
/// Sum the display height of grouped picker entries: a header is one row; a row
|
||||
/// is its label line plus its collapsed summary lines (what the picker draws
|
||||
/// when the row is not expanded).
|
||||
/// Sum the display height of grouped picker entries: a header is one row (plus
|
||||
/// the blank spacer `render_picker_content` draws before non-first headers); a
|
||||
/// row is its label line plus its collapsed summary lines (what the picker
|
||||
/// draws when the row is not expanded).
|
||||
fn measure_entries(entries: &[PickerEntry<'_>]) -> u16 {
|
||||
entries
|
||||
.iter()
|
||||
.map(|e| match e {
|
||||
PickerEntry::Header { .. } => 1u16,
|
||||
.enumerate()
|
||||
.map(|(idx, e)| match e {
|
||||
PickerEntry::Header { .. } => {
|
||||
if idx == 0 {
|
||||
1u16
|
||||
} else {
|
||||
2u16
|
||||
}
|
||||
}
|
||||
PickerEntry::Row(r) => {
|
||||
if r.expanded {
|
||||
1u16.saturating_add(r.description_lines.len() as u16)
|
||||
|
|
@ -678,6 +710,37 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resume_panel_pins_hidden_external_hint_above_scrolling_list() {
|
||||
// More native rows than the panel fits: the hint must stay pinned
|
||||
// above the list instead of scrolling away with it.
|
||||
let mut entries: Vec<_> = (0..20)
|
||||
.map(|i| session_entry(&format!("native-{i}")))
|
||||
.collect();
|
||||
let mut foreign = session_entry("claude-session");
|
||||
foreign.source = "claude".into();
|
||||
entries.push(foreign);
|
||||
let mut a = with_resume(entries);
|
||||
let theme = Theme::current();
|
||||
let area = Rect::new(0, 0, 80, 10);
|
||||
let mut buf = Buffer::empty(area);
|
||||
render(&mut buf, area, &mut a, ListPanel::Resume, &theme);
|
||||
|
||||
let text = buffer_text(&buf);
|
||||
assert!(
|
||||
text.contains("1 external session hidden \u{b7} f to show"),
|
||||
"hidden foreign rows must stay explained while the list scrolls:\n{text}"
|
||||
);
|
||||
assert!(
|
||||
text.find("external session hidden") < text.find("native-"),
|
||||
"the hint must be pinned above the first list row:\n{text}"
|
||||
);
|
||||
assert!(
|
||||
!text.contains("claude-session"),
|
||||
"foreign row stays hidden under the default filter:\n{text}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resume_search_uses_picker_grapheme_viewport_at_narrow_width() {
|
||||
let grapheme = "👩🏽\u{200d}💻";
|
||||
|
|
|
|||
Loading…
Reference in a new issue