mirror of
https://github.com/openai/codex.git
synced 2026-10-27 18:48:37 +00:00
Add a hide shortcut to the agents overview (#44424)
## What changed - Add `Ctrl+W` to hide the selected task without stopping it. Hidden tasks stay hidden through activity and metadata refreshes until explicitly resumed or the TUI restarts. - Expose `agents.hide` in keymap configuration and the shortcut picker, and let the default binding yield to existing custom shortcuts. - Wrap overview footer hints while keeping shortcuts and their labels together when they fit. ## Testing Add coverage for hiding filtered tasks across statuses, visibility through activity and refreshes, explicit resume, existing keybinding compatibility, and custom chord footer rendering at narrow widths. GitOrigin-RevId: 980726e1095b8d5bad8901be919decdde8c0f925
This commit is contained in:
@@ -443,6 +443,8 @@ pub struct TuiAgentsKeymap {
|
||||
pub rename: Option<KeybindingsSpec>,
|
||||
/// Stop the selected running task.
|
||||
pub stop: Option<KeybindingsSpec>,
|
||||
/// Hide the selected task until explicitly resumed or the TUI restarts.
|
||||
pub hide: Option<KeybindingsSpec>,
|
||||
/// Toggle grouping tasks by status or project.
|
||||
pub toggle_grouping: Option<KeybindingsSpec>,
|
||||
}
|
||||
|
||||
@@ -3993,6 +3993,7 @@
|
||||
],
|
||||
"default": {
|
||||
"agents": {
|
||||
"hide": null,
|
||||
"new_task": null,
|
||||
"rename": null,
|
||||
"resume": null,
|
||||
@@ -4303,6 +4304,14 @@
|
||||
"additionalProperties": false,
|
||||
"description": "Shortcuts specific to the shared agents overview.",
|
||||
"properties": {
|
||||
"hide": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Hide the selected task until explicitly resumed or the TUI restarts."
|
||||
},
|
||||
"new_task": {
|
||||
"allOf": [
|
||||
{
|
||||
@@ -4808,6 +4817,7 @@
|
||||
}
|
||||
],
|
||||
"default": {
|
||||
"hide": null,
|
||||
"new_task": null,
|
||||
"rename": null,
|
||||
"resume": null,
|
||||
|
||||
@@ -32,6 +32,8 @@ pub(crate) const AGENTS_OVERVIEW_VIEW_ID: &str = "agents-overview";
|
||||
pub(super) struct AgentsOverviewState {
|
||||
/// Missing metadata records a local resume until the next metadata refresh.
|
||||
pub(super) threads: HashMap<ThreadId, Option<Thread>>,
|
||||
/// Local visibility only; activity and metadata refreshes never reveal hidden roots.
|
||||
pub(super) hidden_threads: HashSet<ThreadId>,
|
||||
pub(super) last_messages: HashMap<ThreadId, String>,
|
||||
pub(super) activity: HashMap<ThreadId, super::agents_overview_details::AgentsOverviewActivity>,
|
||||
pub(super) initialized: bool,
|
||||
@@ -270,6 +272,9 @@ impl App {
|
||||
let Ok(thread_id) = ThreadId::from_string(&root.id) else {
|
||||
continue;
|
||||
};
|
||||
if self.agents_overview.hidden_threads.contains(&thread_id) {
|
||||
continue;
|
||||
}
|
||||
rows.push(AgentsOverviewRow {
|
||||
details: self.agents_overview_details(root, &children),
|
||||
thread: root.clone(),
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[tokio::test]
|
||||
async fn lifecycle_shortcuts_target_filtered_task_in_any_state() {
|
||||
let mut app = make_test_app().await;
|
||||
let mut keymap = TuiKeymap::default();
|
||||
keymap.agents.hide = Some(KeybindingsSpec::One(KeybindingSpec("f7".into())));
|
||||
app.keymap = RuntimeKeymap::from_config(&keymap).unwrap();
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
app.app_event_tx = AppEventSender::new(tx);
|
||||
for status in [
|
||||
ThreadStatus::Idle,
|
||||
ThreadStatus::NotLoaded,
|
||||
ThreadStatus::Active {
|
||||
active_flags: Vec::new(),
|
||||
},
|
||||
] {
|
||||
app.agents_overview
|
||||
.view_state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.focus_composer();
|
||||
let target = ThreadId::new();
|
||||
let mut view = app.agents_overview_view(
|
||||
vec![
|
||||
overview_thread(
|
||||
ThreadId::new(),
|
||||
/*parent_thread_id*/ None,
|
||||
"Other",
|
||||
ThreadStatus::Idle,
|
||||
),
|
||||
overview_thread(target, /*parent_thread_id*/ None, "Target", status),
|
||||
],
|
||||
Some(target),
|
||||
);
|
||||
view.handle_key_event(KeyCode::Esc.into());
|
||||
view.handle_key_event(KeyEvent::new(KeyCode::Char('f'), KeyModifiers::CONTROL));
|
||||
for character in "Target".chars() {
|
||||
view.handle_key_event(KeyCode::Char(character).into());
|
||||
}
|
||||
view.handle_key_event(KeyCode::F(7).into());
|
||||
assert!(
|
||||
matches!(rx.try_recv(), Ok(AppEvent::HideAgentsOverviewThread { thread_id }) if thread_id == target)
|
||||
);
|
||||
assert!(rx.try_recv().is_err());
|
||||
view.handle_key_event(KeyCode::Esc.into());
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn hidden_task_stays_hidden_through_activity_and_seed_until_explicit_resume() -> Result<()> {
|
||||
let (mut app, mut rx, _op_rx) = crate::app::tests::make_test_app_with_channels().await;
|
||||
let mut app_server = crate::start_embedded_app_server_for_picker(&app.config).await?;
|
||||
let started = app_server.start_thread(&app.config).await?;
|
||||
let id = started.session.thread_id;
|
||||
app.enqueue_primary_thread_session(started.session, started.turns)
|
||||
.await?;
|
||||
let thread = overview_thread(
|
||||
id,
|
||||
/*parent_thread_id*/ None,
|
||||
"Hidden task",
|
||||
ThreadStatus::Idle,
|
||||
);
|
||||
app.agents_overview.threads.insert(id, Some(thread.clone()));
|
||||
let mut tui = crate::tui::test_support::make_test_tui()?;
|
||||
let view = app.agents_overview_view(vec![thread.clone()], Some(id));
|
||||
app.chat_widget.show_bottom_pane_view(Box::new(view));
|
||||
app.chat_widget.handle_key_event(KeyCode::Esc.into());
|
||||
app.chat_widget
|
||||
.handle_key_event(KeyEvent::new(KeyCode::Char('w'), KeyModifiers::CONTROL));
|
||||
let hide = std::iter::from_fn(|| rx.try_recv().ok())
|
||||
.find(|event| matches!(event, AppEvent::HideAgentsOverviewThread { .. }))
|
||||
.expect("shortcut requests hiding the task");
|
||||
Box::pin(app.handle_event(&mut tui, &mut app_server, hide)).await?;
|
||||
app.track_agents_overview_notification(&ServerNotification::ThreadStarted(
|
||||
ThreadStartedNotification {
|
||||
thread: thread.clone(),
|
||||
},
|
||||
));
|
||||
app.track_agents_overview_notification(&ServerNotification::ThreadStatusChanged(
|
||||
codex_app_server_protocol::ThreadStatusChangedNotification {
|
||||
thread_id: id.to_string(),
|
||||
status: ThreadStatus::Active {
|
||||
active_flags: Vec::new(),
|
||||
},
|
||||
},
|
||||
));
|
||||
app.track_agents_overview_notification(&ServerNotification::ThreadClosed(
|
||||
ThreadClosedNotification {
|
||||
thread_id: id.to_string(),
|
||||
},
|
||||
));
|
||||
let request_id = Uuid::new_v4();
|
||||
app.agents_overview.initialized = false;
|
||||
app.agents_overview.request_id = Some(request_id);
|
||||
app.apply_agents_overview_thread_refresh(
|
||||
&app_server,
|
||||
request_id,
|
||||
Ok(AgentsOverviewThreadRefresh {
|
||||
threads: HashMap::from([(id, Some(thread.clone()))]),
|
||||
last_messages: HashMap::new(),
|
||||
recent_seed_complete: true,
|
||||
}),
|
||||
);
|
||||
assert_eq!(
|
||||
app.agents_overview_view(vec![thread.clone()], /*selected_thread_id*/ None)
|
||||
.thread_ids(),
|
||||
Vec::<ThreadId>::new()
|
||||
);
|
||||
assert_eq!(app.primary_thread_id, Some(id));
|
||||
app.resume_target_session(
|
||||
&mut tui,
|
||||
&mut app_server,
|
||||
SessionTarget {
|
||||
thread_id: id,
|
||||
path: None,
|
||||
cwd: None,
|
||||
history_mode: None,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(
|
||||
app.agents_overview_view(vec![thread], /*selected_thread_id*/ None)
|
||||
.thread_ids(),
|
||||
vec![id]
|
||||
);
|
||||
app_server.shutdown().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn lifecycle_footer_keeps_custom_chords_with_labels() {
|
||||
let mut app = make_test_app().await;
|
||||
app.keymap = RuntimeKeymap::from_config(
|
||||
&serde_json::from_value(serde_json::json!({
|
||||
"agents": { "hide": "f5 f8" }
|
||||
}))
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut view = app.agents_overview_view(
|
||||
vec![overview_thread(
|
||||
ThreadId::new(),
|
||||
/*parent_thread_id*/ None,
|
||||
"Task",
|
||||
ThreadStatus::Idle,
|
||||
)],
|
||||
/*selected_thread_id*/ None,
|
||||
);
|
||||
view.handle_key_event(KeyCode::Esc.into());
|
||||
for width in [36, 48, 80] {
|
||||
let area = Rect::new(/*x*/ 0, /*y*/ 0, width + 4, /*height*/ 24);
|
||||
let mut buffer = ratatui::buffer::Buffer::empty(area);
|
||||
view.render(area, &mut buffer);
|
||||
let lines = buffer
|
||||
.content()
|
||||
.chunks(usize::from(area.width))
|
||||
.map(|row| {
|
||||
row.iter()
|
||||
.map(ratatui::buffer::Cell::symbol)
|
||||
.collect::<String>()
|
||||
.trim()
|
||||
.to_string()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
assert!(
|
||||
lines
|
||||
.iter()
|
||||
.any(|line| line.contains("hide") && line.contains("f5"))
|
||||
);
|
||||
assert!(
|
||||
lines
|
||||
.iter()
|
||||
.all(|line| unicode_width::UnicodeWidthStr::width(line.as_str())
|
||||
<= usize::from(width))
|
||||
);
|
||||
}
|
||||
app.chat_widget.show_bottom_pane_view(Box::new(view));
|
||||
insta::assert_snapshot!(
|
||||
"agents_custom_lifecycle_chords",
|
||||
render_bottom_popup(&app.chat_widget, /*width*/ 48)
|
||||
.replace(&test_path_display("/tmp/project"), "/tmp/project")
|
||||
);
|
||||
}
|
||||
@@ -25,6 +25,7 @@ impl AgentsOverviewView {
|
||||
&self.agents_keymap.new_task,
|
||||
&self.agents_keymap.rename,
|
||||
&self.agents_keymap.stop,
|
||||
&self.agents_keymap.hide,
|
||||
&self.agents_keymap.toggle_grouping,
|
||||
]
|
||||
.into_iter()
|
||||
@@ -46,15 +47,20 @@ impl AgentsOverviewView {
|
||||
" "
|
||||
},
|
||||
);
|
||||
let mut footer_spans = Vec::new();
|
||||
let mut hints: Vec<Line<'static>> = Vec::new();
|
||||
if !navigation_hint.is_empty() {
|
||||
footer_spans.extend([navigation_hint.bold(), " navigate ".dim()]);
|
||||
hints.push(vec![navigation_hint.bold(), " navigate".dim()].into());
|
||||
}
|
||||
let mut add_hint = |hint: Option<ShortcutHint>, label: &'static str, enabled: bool| {
|
||||
if let Some(hint) = hint {
|
||||
let key = hint.display_label().replace(" + ", "+");
|
||||
footer_spans.push(if enabled { key.bold() } else { key.dim() });
|
||||
footer_spans.push(format!(" {label} ").dim());
|
||||
hints.push(
|
||||
vec![
|
||||
if enabled { key.bold() } else { key.dim() },
|
||||
format!(" {label}").dim(),
|
||||
]
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
};
|
||||
add_hint(
|
||||
@@ -99,16 +105,35 @@ impl AgentsOverviewView {
|
||||
self.selected_row()
|
||||
.is_some_and(|row| matches!(row.thread.status, ThreadStatus::Active { .. })),
|
||||
);
|
||||
add_hint(
|
||||
self.agents_keymap
|
||||
.primary_hint("hide", &self.agents_keymap.hide),
|
||||
"hide",
|
||||
self.selected_row().is_some(),
|
||||
);
|
||||
add_hint(list_hint(ListAction::Cancel), "back", true);
|
||||
let mut footer_line: Line = footer_spans.into();
|
||||
if footer_line.width() > usize::from(width) {
|
||||
for span in &mut footer_line.spans {
|
||||
if span.content.ends_with(" ") {
|
||||
span.content.to_mut().pop();
|
||||
let separator = if hints.iter().map(Line::width).sum::<usize>()
|
||||
+ hints.len().saturating_sub(1) * 2
|
||||
<= usize::from(width)
|
||||
{
|
||||
" "
|
||||
} else {
|
||||
" "
|
||||
};
|
||||
crate::footer_hint::wrap_hint_rows(hints, width, separator.len(), Line::width)
|
||||
.into_iter()
|
||||
.flat_map(|row| {
|
||||
let mut line = Line::default();
|
||||
for hint in row {
|
||||
if !line.spans.is_empty() {
|
||||
line.spans.push(separator.dim());
|
||||
}
|
||||
line.spans.extend(hint.spans);
|
||||
}
|
||||
}
|
||||
}
|
||||
crate::wrapping::word_wrap_lines([footer_line], usize::from(width))
|
||||
// An individual custom chord may be wider than the entire terminal.
|
||||
crate::wrapping::word_wrap_lines([line], usize::from(width.max(1)))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2258,3 +2258,6 @@ async fn command_center_handles_resume_failure_and_success() -> Result<()> {
|
||||
server.shutdown().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[path = "agents_overview_actions_tests.rs"]
|
||||
mod actions;
|
||||
|
||||
@@ -689,6 +689,14 @@ impl BottomPaneView for AgentsOverviewView {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if self.agents_keymap.hide.is_pressed(key) {
|
||||
if let Some(row) = self.selected_row() {
|
||||
self.app_event_tx.send(AppEvent::HideAgentsOverviewThread {
|
||||
thread_id: row.thread_id,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
if self.agents_keymap.stop.is_pressed(key) {
|
||||
if let Some(row) = self.selected_row()
|
||||
&& matches!(row.thread.status, ThreadStatus::Active { .. })
|
||||
|
||||
@@ -2803,6 +2803,10 @@ impl App {
|
||||
}
|
||||
}
|
||||
}
|
||||
AppEvent::HideAgentsOverviewThread { thread_id } => {
|
||||
self.agents_overview.hidden_threads.insert(thread_id);
|
||||
self.repaint_agents_overview();
|
||||
}
|
||||
AppEvent::StopAgentsOverviewThread { thread_id } => {
|
||||
self.stop_agents_overview_thread(app_server, thread_id)
|
||||
.await;
|
||||
|
||||
@@ -1191,6 +1191,10 @@ impl App {
|
||||
target_session: SessionTarget,
|
||||
) -> Result<AppRunControl> {
|
||||
if self.ignore_same_thread_resume(&target_session) {
|
||||
self.agents_overview
|
||||
.hidden_threads
|
||||
.remove(&target_session.thread_id);
|
||||
self.repaint_agents_overview();
|
||||
tui.frame_requester().schedule_frame();
|
||||
return Ok(AppRunControl::Continue);
|
||||
}
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
---
|
||||
source: tui/src/app/agents_overview_actions_tests.rs
|
||||
expression: "render_bottom_popup(&app.chat_widget, 48)"
|
||||
---
|
||||
Agent command center
|
||||
0 need input 0 working 1 ready
|
||||
────────────────────────────────────────────
|
||||
/tmp/project 1
|
||||
› ○ Task Ready
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
New task
|
||||
|
||||
› Describe a new task
|
||||
|
||||
↑↓ navigate ctrl+o resume → open
|
||||
ctrl+n new task ctrl+f search ctrl+s group
|
||||
ctrl+r rename ctrl+x stop f5 f8 hide
|
||||
esc back
|
||||
@@ -1511,6 +1511,7 @@ impl App {
|
||||
self.recap.reset_for_new_thread(Instant::now());
|
||||
}
|
||||
self.primary_thread_id = Some(thread_id);
|
||||
self.agents_overview.hidden_threads.remove(&thread_id);
|
||||
self.agents_overview.threads.entry(thread_id).or_default();
|
||||
self.primary_session_configured = Some(session.clone());
|
||||
self.upsert_agent_picker_thread(
|
||||
|
||||
@@ -310,6 +310,10 @@ pub(crate) enum AppEvent {
|
||||
StopAgentsOverviewThread {
|
||||
thread_id: ThreadId,
|
||||
},
|
||||
/// Hide a dashboard row locally without stopping its task.
|
||||
HideAgentsOverviewThread {
|
||||
thread_id: ThreadId,
|
||||
},
|
||||
/// Start the shared app-server daemon without moving the current embedded session.
|
||||
#[cfg(any(unix, windows))]
|
||||
StartAgentsDaemon,
|
||||
|
||||
@@ -411,6 +411,7 @@ pub(crate) struct AgentsKeymap {
|
||||
pub(crate) new_task: Vec<KeyBinding>,
|
||||
pub(crate) rename: Vec<KeyBinding>,
|
||||
pub(crate) stop: Vec<KeyBinding>,
|
||||
pub(crate) hide: Vec<KeyBinding>,
|
||||
pub(crate) toggle_grouping: Vec<KeyBinding>,
|
||||
chord_hints: Arc<RuntimeChordKeymap>,
|
||||
}
|
||||
@@ -1289,10 +1290,24 @@ impl RuntimeKeymap {
|
||||
new_task: resolve_local!(keymap, defaults, agents, new_task),
|
||||
rename: resolve_local!(keymap, defaults, agents, rename),
|
||||
stop: resolve_local!(keymap, defaults, agents, stop),
|
||||
hide: resolve_local!(keymap, defaults, agents, hide),
|
||||
toggle_grouping: resolve_local!(keymap, defaults, agents, toggle_grouping),
|
||||
chord_hints: Arc::clone(&chords),
|
||||
};
|
||||
|
||||
// The new default yields to existing user bindings in the dashboard.
|
||||
if keymap.agents.hide.is_none()
|
||||
&& (configured_context_alias_is_used(&keymap.agents, "ctrl-w")
|
||||
|| configured_context_alias_is_used(&keymap.list, "ctrl-w")
|
||||
|| configured_context_alias_is_used(&keymap.global, "ctrl-w")
|
||||
|| chords.bindings.iter().any(|chord| {
|
||||
chord.action.context.overlaps(KeymapContext::Agents)
|
||||
&& agents.hide.contains(&chord.chord.prefix)
|
||||
}))
|
||||
{
|
||||
agents.hide.clear();
|
||||
}
|
||||
|
||||
let approval = ApprovalKeymap {
|
||||
open_fullscreen: resolve_local!(keymap, defaults, approval, open_fullscreen),
|
||||
open_thread: resolve_local!(keymap, defaults, approval, open_thread),
|
||||
@@ -1425,6 +1440,7 @@ impl RuntimeKeymap {
|
||||
(keymap.agents.new_task.as_ref(), &mut agents.new_task),
|
||||
(keymap.agents.rename.as_ref(), &mut agents.rename),
|
||||
(keymap.agents.stop.as_ref(), &mut agents.stop),
|
||||
(keymap.agents.hide.as_ref(), &mut agents.hide),
|
||||
(
|
||||
keymap.agents.toggle_grouping.as_ref(),
|
||||
&mut agents.toggle_grouping,
|
||||
@@ -1789,6 +1805,7 @@ impl RuntimeKeymap {
|
||||
new_task: default_bindings![ctrl(KeyCode::Char('n'))],
|
||||
rename: default_bindings![ctrl(KeyCode::Char('r'))],
|
||||
stop: default_bindings![ctrl(KeyCode::Char('x'))],
|
||||
hide: default_bindings![ctrl(KeyCode::Char('w'))],
|
||||
toggle_grouping: default_bindings![ctrl(KeyCode::Char('s'))],
|
||||
chord_hints: Arc::default(),
|
||||
},
|
||||
|
||||
@@ -398,6 +398,7 @@ define_runtime_action_bindings! {
|
||||
new_task,
|
||||
rename,
|
||||
stop,
|
||||
hide,
|
||||
toggle_grouping,
|
||||
],
|
||||
"approval" => Approval, approval, approval [
|
||||
|
||||
@@ -37,3 +37,19 @@ See the Codex keymap documentation for supported actions and examples."
|
||||
config.remove(context);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_agents_defaults_preserve_existing_custom_bindings() {
|
||||
for context in ["agents", "list"] {
|
||||
let existing = if context == "agents" {
|
||||
"stop"
|
||||
} else {
|
||||
"move_down"
|
||||
};
|
||||
let keymap: TuiKeymap =
|
||||
serde_json::from_value(json!({context: {existing: "ctrl-w"}})).unwrap();
|
||||
let runtime =
|
||||
RuntimeKeymap::from_config(&keymap).expect("existing configuration remains valid");
|
||||
assert!(runtime.agents.hide.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +218,7 @@ pub(super) const KEYMAP_ACTIONS: &[KeymapActionDescriptor] = &[
|
||||
action("agents", "Agents", "new_task", "Start composing a new agent task."),
|
||||
action("agents", "Agents", "rename", "Rename the selected task."),
|
||||
action("agents", "Agents", "stop", "Stop the selected running task."),
|
||||
action("agents", "Agents", "hide", "Hide the selected task until explicitly resumed."),
|
||||
action("agents", "Agents", "toggle_grouping", "Group tasks by status or project."),
|
||||
action("approval", "Approval", "open_fullscreen", "Open approval details fullscreen."),
|
||||
action("approval", "Approval", "open_thread", "Open the approval source thread when available."),
|
||||
@@ -395,6 +396,7 @@ pub(super) fn binding_slot<'a>(
|
||||
("agents", "new_task") => Some(&mut keymap.agents.new_task),
|
||||
("agents", "rename") => Some(&mut keymap.agents.rename),
|
||||
("agents", "stop") => Some(&mut keymap.agents.stop),
|
||||
("agents", "hide") => Some(&mut keymap.agents.hide),
|
||||
("agents", "toggle_grouping") => Some(&mut keymap.agents.toggle_grouping),
|
||||
("approval", "open_fullscreen") => Some(&mut keymap.approval.open_fullscreen),
|
||||
("approval", "open_thread") => Some(&mut keymap.approval.open_thread),
|
||||
|
||||
+4
-4
@@ -18,11 +18,11 @@ expression: "render_bottom_popup(&app.chat_widget, 48)"
|
||||
|
||||
|
||||
|
||||
|
||||
New task
|
||||
|
||||
› Describe a new task
|
||||
|
||||
↑↓ navigate ctrl+o resume → open ctrl+n new
|
||||
task ctrl+f search ctrl+s group ctrl+r
|
||||
rename ctrl+x stop esc back
|
||||
↑↓ navigate ctrl+o resume → open
|
||||
ctrl+n new task ctrl+f search ctrl+s group
|
||||
ctrl+r rename ctrl+x stop ctrl+w hide
|
||||
esc back
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ expression: "render_picker(params, 120)"
|
||||
|
||||
Keymap
|
||||
All configurable shortcuts.
|
||||
142 actions, 1 customized, 5 unbound.
|
||||
143 actions, 1 customized, 5 unbound.
|
||||
|
||||
[All] Common Customized (1) Unbound (5) App Composer Editor Vim Navigation Agents Approval Debug
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ expression: "render_picker(params, 120)"
|
||||
|
||||
Keymap
|
||||
All configurable shortcuts.
|
||||
143 actions, 0 customized, 6 unbound.
|
||||
144 actions, 0 customized, 6 unbound.
|
||||
|
||||
[All] Common Customized (0) Unbound (6) App Composer Editor Vim Navigation Agents Approval Debug
|
||||
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
source: tui/src/keymap_setup.rs
|
||||
expression: snapshot
|
||||
---
|
||||
tab: All (142 selectable)
|
||||
tab: All (143 selectable)
|
||||
tab: Common (20 selectable)
|
||||
tab: Customized (0) (0 selectable)
|
||||
tab: Unbound (5) (5 selectable)
|
||||
@@ -11,7 +11,7 @@ tab: Composer (5 selectable)
|
||||
tab: Editor (17 selectable)
|
||||
tab: Vim (69 selectable)
|
||||
tab: Navigation (20 selectable)
|
||||
tab: Agents (6 selectable)
|
||||
tab: Agents (7 selectable)
|
||||
tab: Approval (8 selectable)
|
||||
tab: Debug (1 selectable)
|
||||
Open Agents | unbound | Global open_agents Open Agents Open the shared agent-session overview. unbound Default
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ expression: "render_picker(params, 120)"
|
||||
|
||||
Keymap
|
||||
All configurable shortcuts.
|
||||
142 actions, 1 customized, 6 unbound.
|
||||
143 actions, 1 customized, 6 unbound.
|
||||
|
||||
[All] Common Customized (1) Unbound (6) App Composer Editor Vim Navigation Agents Approval Debug
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ expression: "render_picker(params, 78)"
|
||||
|
||||
Keymap
|
||||
All configurable shortcuts.
|
||||
142 actions, 0 customized, 5 unbound.
|
||||
143 actions, 0 customized, 5 unbound.
|
||||
|
||||
[All] Common Customized (0) Unbound (5) App Composer Editor Vim
|
||||
Navigation Agents Approval Debug
|
||||
|
||||
@@ -5,7 +5,7 @@ expression: "render_picker(params, 120)"
|
||||
|
||||
Keymap
|
||||
All configurable shortcuts.
|
||||
142 actions, 0 customized, 5 unbound.
|
||||
143 actions, 0 customized, 5 unbound.
|
||||
|
||||
[All] Common Customized (0) Unbound (5) App Composer Editor Vim Navigation Agents Approval Debug
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ expression: "render_picker(params, 120)"
|
||||
|
||||
Keymap
|
||||
All configurable shortcuts.
|
||||
142 actions, 0 customized, 5 unbound.
|
||||
143 actions, 0 customized, 5 unbound.
|
||||
|
||||
[All] Common Customized (0) Unbound (5) App Composer Editor Vim Navigation Agents Approval Debug
|
||||
|
||||
|
||||
Reference in New Issue
Block a user