Synced from monorepo
Changes: - Persist submitter identity for /feedback - Let custom models use rotating tokens from named auth providers - Template stale tool/param name literals in server-native descriptions - Minimal mode commits thinking in full, lookups as one-liners - Tighten durable append internals - Nudge model to end turn on no-op bash commands - Per-fetch signing nonce in the managed-config envelope, with a server-side replay probe - Include working tree in startup status
This commit is contained in:
parent
ba76b0a683
commit
a881e6703f
140 changed files with 6746 additions and 2377 deletions
|
|
@ -6040,9 +6040,10 @@ mod tests {
|
|||
);
|
||||
assert!(
|
||||
lines.iter().any(|l| matches!(
|
||||
l,
|
||||
DashboardLine::Header { state, count } if *state == RowState::Working && *count == 2
|
||||
)),
|
||||
l,
|
||||
DashboardLine::Header { state, count }
|
||||
if *state == RowState::Working && *count == 2
|
||||
)),
|
||||
"collapsed Working header must still render with its true count",
|
||||
);
|
||||
let working_rows = lines
|
||||
|
|
@ -6080,7 +6081,8 @@ mod tests {
|
|||
assert!(
|
||||
lines
|
||||
.iter()
|
||||
.any(|l| matches!(l, DashboardLine::PinnedHeader { count } if *count == 1)),
|
||||
.any(|l| matches!(l, DashboardLine::PinnedHeader { count }
|
||||
if *count == 1)),
|
||||
"collapsed Pinned header must still render",
|
||||
);
|
||||
// The pinned row is hidden; the (non-pinned) Working row remains.
|
||||
|
|
@ -6142,9 +6144,10 @@ mod tests {
|
|||
// Header still shows the TRUE total, not the visible count.
|
||||
assert!(
|
||||
lines.iter().any(|l| matches!(
|
||||
l,
|
||||
DashboardLine::Header { state, count } if *state == RowState::Idle && *count == total as usize
|
||||
)),
|
||||
l,
|
||||
DashboardLine::Header { state, count }
|
||||
if *state == RowState::Idle && *count == total as usize
|
||||
)),
|
||||
"Idle header keeps the true total count",
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -595,7 +595,8 @@ fn render_file_list(buf: &mut Buffer, area: Rect, state: &mut MemoryModalState,
|
|||
);
|
||||
|
||||
if is_selected
|
||||
&& matches!(state.mode, MemoryModalMode::ConfirmingDelete { idx } if idx == filt_idx)
|
||||
&& matches!(state.mode, MemoryModalMode::ConfirmingDelete { idx }
|
||||
if idx == filt_idx)
|
||||
{
|
||||
let hint = " [x to confirm]";
|
||||
let hint_w = hint.len() as u16;
|
||||
|
|
|
|||
|
|
@ -275,21 +275,20 @@ impl SettingsModalState {
|
|||
self.invalidate_filter();
|
||||
|
||||
if let Some(key) = subpane_key {
|
||||
let still_visible = self
|
||||
.rows
|
||||
.iter()
|
||||
.any(|r| matches!(r, RowEntry::Setting { key: k, .. } if *k == key));
|
||||
let still_visible = self.rows.iter().any(|r| {
|
||||
matches!(r, RowEntry::Setting { key: k, .. }
|
||||
if *k == key)
|
||||
});
|
||||
if !still_visible {
|
||||
self.transition_to_browse();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(key) = prev_key {
|
||||
if let Some(idx) = self
|
||||
.rows
|
||||
.iter()
|
||||
.position(|r| matches!(r, RowEntry::Setting { key: k, .. } if *k == key))
|
||||
{
|
||||
if let Some(idx) = self.rows.iter().position(|r| {
|
||||
matches!(r, RowEntry::Setting { key: k, .. }
|
||||
if *k == key)
|
||||
}) {
|
||||
self.selected = idx;
|
||||
} else {
|
||||
self.selected = self
|
||||
|
|
|
|||
|
|
@ -36,13 +36,17 @@ fn contextual_hints_group_sub_sheet_flow() {
|
|||
let group_idx = s
|
||||
.rows
|
||||
.iter()
|
||||
.position(|r| matches!(r, RowEntry::Setting { key, .. } if *key == "contextual_hints"))
|
||||
.position(|r| {
|
||||
matches!(r, RowEntry::Setting { key, .. }
|
||||
if *key == "contextual_hints")
|
||||
})
|
||||
.expect("group row present");
|
||||
assert!(
|
||||
!s.rows.iter().any(|r| matches!(
|
||||
r,
|
||||
RowEntry::Setting { key, .. } if key.starts_with("contextual_hints.")
|
||||
)),
|
||||
r,
|
||||
RowEntry::Setting { key, .. }
|
||||
if key.starts_with("contextual_hints.")
|
||||
)),
|
||||
"child rows must be hidden from the top-level list",
|
||||
);
|
||||
|
||||
|
|
@ -4118,7 +4122,10 @@ fn advance_next_recovers_when_selection_is_hidden() {
|
|||
let compact_idx = s
|
||||
.rows
|
||||
.iter()
|
||||
.position(|r| matches!(r, RowEntry::Setting { key, .. } if *key == "compact_mode"))
|
||||
.position(|r| {
|
||||
matches!(r, RowEntry::Setting { key, .. }
|
||||
if *key == "compact_mode")
|
||||
})
|
||||
.unwrap();
|
||||
s.selected = compact_idx;
|
||||
// Advance: lands on the first visible setting (show_timestamps).
|
||||
|
|
@ -4127,7 +4134,10 @@ fn advance_next_recovers_when_selection_is_hidden() {
|
|||
let show_ts_idx = s
|
||||
.rows
|
||||
.iter()
|
||||
.position(|r| matches!(r, RowEntry::Setting { key, .. } if *key == "show_timestamps"))
|
||||
.position(|r| {
|
||||
matches!(r, RowEntry::Setting { key, .. }
|
||||
if *key == "show_timestamps")
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(s.selected, show_ts_idx);
|
||||
}
|
||||
|
|
@ -4150,7 +4160,10 @@ fn advance_prev_recovers_when_selection_is_hidden() {
|
|||
let compact_idx = s
|
||||
.rows
|
||||
.iter()
|
||||
.position(|r| matches!(r, RowEntry::Setting { key, .. } if *key == "compact_mode"))
|
||||
.position(|r| {
|
||||
matches!(r, RowEntry::Setting { key, .. }
|
||||
if *key == "compact_mode")
|
||||
})
|
||||
.unwrap();
|
||||
s.selected = compact_idx;
|
||||
let moved = s.advance_prev();
|
||||
|
|
@ -4158,7 +4171,10 @@ fn advance_prev_recovers_when_selection_is_hidden() {
|
|||
let simple_idx = s
|
||||
.rows
|
||||
.iter()
|
||||
.position(|r| matches!(r, RowEntry::Setting { key, .. } if *key == "simple_mode"))
|
||||
.position(|r| {
|
||||
matches!(r, RowEntry::Setting { key, .. }
|
||||
if *key == "simple_mode")
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(s.selected, simple_idx);
|
||||
}
|
||||
|
|
@ -4258,10 +4274,10 @@ fn section_headers_have_blank_line_above_except_first() {
|
|||
for cat in SettingCategory::ALL {
|
||||
// Skip categories the default registry doesn't populate
|
||||
// (e.g. Session — no settings registered).
|
||||
let has_setting = s
|
||||
.rows
|
||||
.iter()
|
||||
.any(|r| matches!(r, RowEntry::Header { category } if category == cat));
|
||||
let has_setting = s.rows.iter().any(|r| {
|
||||
matches!(r, RowEntry::Header { category }
|
||||
if category == cat)
|
||||
});
|
||||
if !has_setting {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -4607,7 +4623,10 @@ fn two_line_row_hit_rect_spans_both_lines() {
|
|||
let row_idx = s
|
||||
.rows
|
||||
.iter()
|
||||
.position(|r| matches!(r, RowEntry::Setting { key, .. } if *key == "coding_data_sharing"))
|
||||
.position(|r| {
|
||||
matches!(r, RowEntry::Setting { key, .. }
|
||||
if *key == "coding_data_sharing")
|
||||
})
|
||||
.expect("coding_data_sharing must be registered");
|
||||
// Render at a narrow width so coding_data_sharing forces a
|
||||
// two-line layout.
|
||||
|
|
@ -4672,7 +4691,10 @@ fn two_line_row_with_expansion_renders_three_segments() {
|
|||
let row_idx = s
|
||||
.rows
|
||||
.iter()
|
||||
.position(|r| matches!(r, RowEntry::Setting { key, .. } if *key == "coding_data_sharing"))
|
||||
.position(|r| {
|
||||
matches!(r, RowEntry::Setting { key, .. }
|
||||
if *key == "coding_data_sharing")
|
||||
})
|
||||
.expect("coding_data_sharing must be registered");
|
||||
s.selected = row_idx;
|
||||
s.expanded_keys.insert("coding_data_sharing");
|
||||
|
|
@ -4730,7 +4752,10 @@ fn group_row_renders_expanded_description() {
|
|||
let row_idx = s
|
||||
.rows
|
||||
.iter()
|
||||
.position(|r| matches!(r, RowEntry::Setting { key, .. } if *key == "contextual_hints"))
|
||||
.position(|r| {
|
||||
matches!(r, RowEntry::Setting { key, .. }
|
||||
if *key == "contextual_hints")
|
||||
})
|
||||
.expect("contextual_hints group must be registered");
|
||||
s.selected = row_idx;
|
||||
s.expanded_keys.insert("contextual_hints");
|
||||
|
|
@ -5769,7 +5794,10 @@ fn enter_picker_for(key: &'static str) -> SettingsModalState {
|
|||
let row_idx = s
|
||||
.rows
|
||||
.iter()
|
||||
.position(|r| matches!(r, RowEntry::Setting { key: k, .. } if *k == key))
|
||||
.position(|r| {
|
||||
matches!(r, RowEntry::Setting { key: k, .. }
|
||||
if *k == key)
|
||||
})
|
||||
.unwrap_or_else(|| panic!("no row for key `{key}` in default registry"));
|
||||
assert!(s.select_at(row_idx), "select_at({row_idx})");
|
||||
assert!(
|
||||
|
|
|
|||
|
|
@ -1681,9 +1681,10 @@ mod tests {
|
|||
let entries = build_entries(&all_contexts(), ®istry, true);
|
||||
let has_row = entries.iter().any(|e| {
|
||||
matches!(
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, .. } if item.label == "mouse reporting"
|
||||
)
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, .. }
|
||||
if item.label == "mouse reporting"
|
||||
)
|
||||
});
|
||||
assert!(
|
||||
!has_row,
|
||||
|
|
@ -1776,21 +1777,24 @@ mod tests {
|
|||
|
||||
let has_todos = entries.iter().any(|e| {
|
||||
matches!(
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, .. } if item.label == "todos"
|
||||
)
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, .. }
|
||||
if item.label == "todos"
|
||||
)
|
||||
});
|
||||
let has_sessions = entries.iter().any(|e| {
|
||||
matches!(
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, .. } if item.label == "sessions"
|
||||
)
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, .. }
|
||||
if item.label == "sessions"
|
||||
)
|
||||
});
|
||||
let has_queue = entries.iter().any(|e| {
|
||||
matches!(
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, .. } if item.label == "queue"
|
||||
)
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, .. }
|
||||
if item.label == "queue"
|
||||
)
|
||||
});
|
||||
assert!(has_todos, "should include toggle todos");
|
||||
assert!(has_sessions, "should include open sessions");
|
||||
|
|
@ -1835,13 +1839,14 @@ mod tests {
|
|||
.iter()
|
||||
.find(|e| {
|
||||
matches!(
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint {
|
||||
item,
|
||||
action_id: None,
|
||||
..
|
||||
} if item.label == "paste"
|
||||
)
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint {
|
||||
item,
|
||||
action_id: None,
|
||||
..
|
||||
}
|
||||
if item.label == "paste"
|
||||
)
|
||||
})
|
||||
.expect("cheatsheet should list paste");
|
||||
let ShortcutsHelpEntry::Hint {
|
||||
|
|
@ -1922,9 +1927,10 @@ mod tests {
|
|||
|
||||
let nav_dimmed = entries.iter().any(|e| {
|
||||
matches!(
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, dimmed: true, .. } if item.label == "nav"
|
||||
)
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, dimmed: true, .. }
|
||||
if item.label == "nav"
|
||||
)
|
||||
});
|
||||
assert!(
|
||||
nav_dimmed,
|
||||
|
|
@ -1933,17 +1939,19 @@ mod tests {
|
|||
|
||||
let quit_bright = entries.iter().any(|e| {
|
||||
matches!(
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, dimmed: false, .. } if item.label == "quit"
|
||||
)
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, dimmed: false, .. }
|
||||
if item.label == "quit"
|
||||
)
|
||||
});
|
||||
assert!(quit_bright, "quit should not be dimmed (When::Always)");
|
||||
|
||||
let cancel_bright = entries.iter().any(|e| {
|
||||
matches!(
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, dimmed: false, .. } if item.label == "cancel"
|
||||
)
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, dimmed: false, .. }
|
||||
if item.label == "cancel"
|
||||
)
|
||||
});
|
||||
assert!(
|
||||
cancel_bright,
|
||||
|
|
@ -1959,9 +1967,10 @@ mod tests {
|
|||
|
||||
let send_dimmed = entries.iter().any(|e| {
|
||||
matches!(
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, dimmed: true, .. } if item.label == "send"
|
||||
)
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, dimmed: true, .. }
|
||||
if item.label == "send"
|
||||
)
|
||||
});
|
||||
assert!(
|
||||
send_dimmed,
|
||||
|
|
@ -1970,9 +1979,10 @@ mod tests {
|
|||
|
||||
let nav_dimmed = entries.iter().any(|e| {
|
||||
matches!(
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, dimmed: true, .. } if item.label == "nav"
|
||||
)
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, dimmed: true, .. }
|
||||
if item.label == "nav"
|
||||
)
|
||||
});
|
||||
assert!(
|
||||
nav_dimmed,
|
||||
|
|
@ -2638,14 +2648,15 @@ mod tests {
|
|||
.iter()
|
||||
.position(|e| {
|
||||
matches!(
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint {
|
||||
item,
|
||||
action_id: None,
|
||||
long_help: Some(_),
|
||||
..
|
||||
} if item.label == "paste"
|
||||
)
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint {
|
||||
item,
|
||||
action_id: None,
|
||||
long_help: Some(_),
|
||||
..
|
||||
}
|
||||
if item.label == "paste"
|
||||
)
|
||||
})
|
||||
.expect("paste pseudo-row with long_help");
|
||||
assert_eq!(
|
||||
|
|
@ -3024,9 +3035,10 @@ mod tests {
|
|||
for label in ["top", "btm", "copy", "copy cmd"] {
|
||||
let present = entries.iter().any(|e| {
|
||||
matches!(
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, .. } if item.label == label
|
||||
)
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint { item, .. }
|
||||
if item.label == label
|
||||
)
|
||||
});
|
||||
assert!(
|
||||
!present,
|
||||
|
|
@ -3315,14 +3327,15 @@ mod tests {
|
|||
.iter()
|
||||
.position(|e| {
|
||||
matches!(
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint {
|
||||
item,
|
||||
action_id: None,
|
||||
long_help: Some(_),
|
||||
..
|
||||
} if item.label == "paste"
|
||||
)
|
||||
e,
|
||||
ShortcutsHelpEntry::Hint {
|
||||
item,
|
||||
action_id: None,
|
||||
long_help: Some(_),
|
||||
..
|
||||
}
|
||||
if item.label == "paste"
|
||||
)
|
||||
})
|
||||
.expect("paste pseudo-row with long_help");
|
||||
let key_id = ExpandKey::Pseudo("paste");
|
||||
|
|
|
|||
|
|
@ -992,11 +992,10 @@ impl TasksPane {
|
|||
};
|
||||
if changed {
|
||||
self.rebuild_entries();
|
||||
if let Some(header) = self
|
||||
.entries
|
||||
.iter()
|
||||
.find(|e| matches!(e, TaskEntry::Header { group: g, .. } if *g == group))
|
||||
{
|
||||
if let Some(header) = self.entries.iter().find(|e| {
|
||||
matches!(e, TaskEntry::Header { group: g, .. }
|
||||
if *g == group)
|
||||
}) {
|
||||
let id = header.stable_id();
|
||||
self.list_state.select_by_id(id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2949,10 +2949,12 @@ mod tests {
|
|||
|
||||
// Verify headers
|
||||
assert!(
|
||||
matches!(&result[0], crate::views::picker::PickerEntry::Header { label } if label == &"fw-1")
|
||||
matches!(&result[0], crate::views::picker::PickerEntry::Header { label }
|
||||
if label == &"fw-1")
|
||||
);
|
||||
assert!(
|
||||
matches!(&result[2], crate::views::picker::PickerEntry::Header { label } if label == &"xai")
|
||||
matches!(&result[2], crate::views::picker::PickerEntry::Header { label }
|
||||
if label == &"xai")
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -2981,11 +2983,13 @@ mod tests {
|
|||
Some("zzz"),
|
||||
);
|
||||
assert!(
|
||||
matches!(&result[0], crate::views::picker::PickerEntry::Header { label } if label == &"zzz"),
|
||||
matches!(&result[0], crate::views::picker::PickerEntry::Header { label }
|
||||
if label == &"zzz"),
|
||||
"current repo group pinned first"
|
||||
);
|
||||
assert!(
|
||||
matches!(&result[2], crate::views::picker::PickerEntry::Header { label } if label == &"aaa"),
|
||||
matches!(&result[2], crate::views::picker::PickerEntry::Header { label }
|
||||
if label == &"aaa"),
|
||||
"remaining group follows alphabetically"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue