Synced from monorepo
Changes: - Gate session-lifecycle heap steady state with a dhat soak - Unbreak merge lifecycle e2e after default model → grok-4.5 - Scan home-scope rules dirs at <root>/rules - Complete text-input paste and terminal parity - Gate project roles and personas - Use canonical editing in dialogs - Use canonical editing in search bars - Reject ambiguous MCP tool IDs - Harden Git operands for plugins - Simplify queue drain API - Pass RFC 9207 iss through MCP OAuth token exchange - Show leader roster when local agents map is empty - Use canonical editing in Persona views - Remove marketplace default-skills auto-install and purge old installs - Use canonical editing in extension forms - Add canonical dashboard text editing - Use canonical editing in settings - Add /summarize as a /recap alias - Restore previous agent when exiting dashboard - Use tool_choice auto for compaction - Settings toggle for snap-prompt-to-top on send - Update default models to grok-4.5 - Source login shell once for local bash (env + alias/function snapshot) - Template hardcoded param names in server-native tool descriptions - Fix System-Reminder XML tag injection in CLAUDE.md via agents_md - Fix remote workspace-server hardcoding LSP trust (repo code execution risk) - Clear orphaned tool-call updates at turn end - Suppress task wake after cancel - Send x-grok-client-identifier on direct API tool calls - Harden dashboard peek lease transitions - Host /btw side panel in live region (minimal mode) - Bound scroll presentation latency - Highlight multi-line constructs correctly in diffs and the file viewer - Block web_fetch non-public IPs; local opt-in is explicit-host only - Seed coding_data_retention_opt_out=false for OAuth e2es in pty-harness - Follow up clipboard delivery feedback - Use canonical editing in pickers - Route TextArea through canonical editor - Persistent "watching" status row; quieter turn markers - Gate sensitive edit targets - Expose agent registry counts and gate session churn on them - Default coding data sharing to opt-out until server preference applies - Wire chat attachment ids through gateway prompts - On auth refresh failure, issue retry - Forward preview provenance and computer lifecycle state - Document independent privacy controls and scope /privacy output - Strip SamplingError Display prefix on rate-limit UI copy - Stop dumping Cloudflare HTML into Retry failed - Disable in-place prompt edit (scroll jank on enter) - Strip forced ANSI color from gh pr view JSON - Plumb bash tool description onto ToolUsageCard wire
This commit is contained in:
parent
98c3b2438a
commit
7cfcb20d2b
292 changed files with 23315 additions and 9209 deletions
|
|
@ -389,12 +389,25 @@ pub(crate) fn build_virtual_list(
|
|||
items
|
||||
}
|
||||
|
||||
/// Build a position-indexed entry map for the session picker.
|
||||
///
|
||||
/// Each element is `Some(item)` for selectable rows or `None` for
|
||||
/// non-selectable headers. When `grouped` is true, repo-group headers
|
||||
/// are interleaved so indices match what the renderer stores in hit areas.
|
||||
/// `current_repo` pins the matching repo group to the top of the list.
|
||||
/// Rebuild expansion keys in the backing-data index space used by session rendering.
|
||||
pub(crate) fn expand_all_mapped_session_items(
|
||||
state: &mut PickerState,
|
||||
entry_map: &[Option<PickerItem>],
|
||||
) {
|
||||
state.expanded.clear();
|
||||
if state.query().is_empty() {
|
||||
return;
|
||||
}
|
||||
for item in entry_map.iter().flatten() {
|
||||
let key = match item {
|
||||
PickerItem::Fuzzy { original_index } => *original_index,
|
||||
PickerItem::Content { hit_index } => CONTENT_EXPAND_OFFSET + hit_index,
|
||||
};
|
||||
state.expanded.insert(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the position-indexed session map, including non-selectable headers.
|
||||
pub(crate) fn build_entry_map(
|
||||
entries: Option<&[SessionPickerEntry]>,
|
||||
content_results: Option<&[xai_grok_shell::extensions::session_search::SearchSessionHit]>,
|
||||
|
|
@ -489,6 +502,77 @@ pub(crate) fn build_entry_map(
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) enum SessionPickerWorktreeSelection {
|
||||
Fuzzy(usize),
|
||||
Content { session_id: String, cwd: String },
|
||||
Unavailable,
|
||||
}
|
||||
|
||||
/// Resolve Ctrl+W before generic editing because the line editor binds it to delete-word.
|
||||
pub(crate) fn session_picker_worktree_selection(
|
||||
key: &crossterm::event::KeyEvent,
|
||||
state: &mut PickerState,
|
||||
entry_map: &[Option<PickerItem>],
|
||||
non_selectable: &[bool],
|
||||
entries: Option<&[SessionPickerEntry]>,
|
||||
content_results: Option<&[xai_grok_shell::extensions::session_search::SearchSessionHit]>,
|
||||
) -> Option<SessionPickerWorktreeSelection> {
|
||||
if key.kind != crossterm::event::KeyEventKind::Press || !crate::key!('w', CONTROL).matches(key)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
if entry_map.is_empty() {
|
||||
return Some(SessionPickerWorktreeSelection::Unavailable);
|
||||
}
|
||||
crate::views::picker::clamp_picker_selection(state, entry_map.len(), non_selectable);
|
||||
Some(
|
||||
match entry_map
|
||||
.get(state.selected)
|
||||
.and_then(|entry| entry.as_ref())
|
||||
{
|
||||
Some(PickerItem::Fuzzy { original_index }) => entries
|
||||
.and_then(|entries| entries.get(*original_index))
|
||||
.filter(|entry| !crate::app::is_foreign_picker_source(&entry.source))
|
||||
.map_or(SessionPickerWorktreeSelection::Unavailable, |_| {
|
||||
SessionPickerWorktreeSelection::Fuzzy(*original_index)
|
||||
}),
|
||||
Some(PickerItem::Content { hit_index }) => content_results
|
||||
.and_then(|results| results.get(*hit_index))
|
||||
.map_or(SessionPickerWorktreeSelection::Unavailable, |hit| {
|
||||
SessionPickerWorktreeSelection::Content {
|
||||
session_id: hit.session_id.clone(),
|
||||
cwd: hit.cwd.clone(),
|
||||
}
|
||||
}),
|
||||
None => SessionPickerWorktreeSelection::Unavailable,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Rebuild backing-index expansion after a session query changes.
|
||||
pub(crate) fn sync_session_picker_query_expansion(
|
||||
entries: Option<&[SessionPickerEntry]>,
|
||||
content_results: Option<&[xai_grok_shell::extensions::session_search::SearchSessionHit]>,
|
||||
entries_query: Option<&str>,
|
||||
state: &mut PickerState,
|
||||
grouped: bool,
|
||||
content_loading: bool,
|
||||
source_filter: SourceFilter,
|
||||
current_repo: Option<&str>,
|
||||
) {
|
||||
let entry_map = build_entry_map(
|
||||
entries,
|
||||
content_results,
|
||||
effective_filter_query(state.query(), entries_query),
|
||||
grouped,
|
||||
content_loading,
|
||||
source_filter,
|
||||
current_repo,
|
||||
);
|
||||
expand_all_mapped_session_items(state, &entry_map);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Session entry data building
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -1096,6 +1180,28 @@ mod tests {
|
|||
assert!(matches!(map[3], Some(PickerItem::Content { hit_index: 0 })));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expand_all_mapped_session_items_uses_backing_indices() {
|
||||
let entries = vec![make_entry("zero", "repo-a"), make_entry("needle", "repo-b")];
|
||||
let hits = vec![make_content_hit("content")];
|
||||
let map = build_entry_map(
|
||||
Some(&entries),
|
||||
Some(&hits),
|
||||
"needle",
|
||||
true,
|
||||
false,
|
||||
SourceFilter::All,
|
||||
None,
|
||||
);
|
||||
let mut state = PickerState::default();
|
||||
state.set_query("needle");
|
||||
|
||||
expand_all_mapped_session_items(&mut state, &map);
|
||||
|
||||
assert_eq!(state.expanded, HashSet::from([1, CONTENT_EXPAND_OFFSET]),);
|
||||
assert!(!state.expanded.contains(&0), "group header is not an item");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foreign_id_does_not_suppress_native_content_result() {
|
||||
let mut foreign = make_entry("shared", "repo");
|
||||
|
|
|
|||
Loading…
Reference in a new issue