mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Apply argument comment lint across codex-rs (#14652)
## Why Once the repo-local lint exists, `codex-rs` needs to follow the checked-in convention and CI needs to keep it from drifting. This commit applies the fallback `/*param*/` style consistently across existing positional literal call sites without changing those APIs. The longer-term preference is still to avoid APIs that require comments by choosing clearer parameter types and call shapes. This PR is intentionally the mechanical follow-through for the places where the existing signatures stay in place. After rebasing onto newer `main`, the rollout also had to cover newly introduced `tui_app_server` call sites. That made it clear the first cut of the CI job was too expensive for the common path: it was spending almost as much time installing `cargo-dylint` and re-testing the lint crate as a representative test job spends running product tests. The CI update keeps the full workspace enforcement but trims that extra overhead from ordinary `codex-rs` PRs. ## What changed - keep a dedicated `argument_comment_lint` job in `rust-ci` - mechanically annotate remaining opaque positional literals across `codex-rs` with exact `/*param*/` comments, including the rebased `tui_app_server` call sites that now fall under the lint - keep the checked-in style aligned with the lint policy by using `/*param*/` and leaving string and char literals uncommented - cache `cargo-dylint`, `dylint-link`, and the relevant Cargo registry/git metadata in the lint job - split changed-path detection so the lint crate's own `cargo test` step runs only when `tools/argument-comment-lint/*` or `rust-ci.yml` changes - continue to run the repo wrapper over the `codex-rs` workspace, so product-code enforcement is unchanged Most of the code changes in this commit are intentionally mechanical comment rewrites or insertions driven by the lint itself. ## Verification - `./tools/argument-comment-lint/run.sh --workspace` - `cargo test -p codex-tui-app-server -p codex-tui` - parsed `.github/workflows/rust-ci.yml` locally with PyYAML --- * -> #14652 * #14651
This commit is contained in:
+62
-39
@@ -236,10 +236,10 @@ fn emit_skill_load_warnings(app_event_tx: &AppEventSender, errors: &[SkillErrorI
|
||||
fn emit_project_config_warnings(app_event_tx: &AppEventSender, config: &Config) {
|
||||
let mut disabled_folders = Vec::new();
|
||||
|
||||
for layer in config
|
||||
.config_layer_stack
|
||||
.get_layers(ConfigLayerStackOrdering::LowestPrecedenceFirst, true)
|
||||
{
|
||||
for layer in config.config_layer_stack.get_layers(
|
||||
ConfigLayerStackOrdering::LowestPrecedenceFirst,
|
||||
/*include_disabled*/ true,
|
||||
) {
|
||||
let ConfigLayerSource::Project { dot_codex_folder } = &layer.name else {
|
||||
continue;
|
||||
};
|
||||
@@ -1127,8 +1127,10 @@ impl App {
|
||||
}
|
||||
|
||||
if let Some(label) = permissions_history_label {
|
||||
self.chat_widget
|
||||
.add_info_message(format!("Permissions updated to {label}"), None);
|
||||
self.chat_widget.add_info_message(
|
||||
format!("Permissions updated to {label}"),
|
||||
/*hint*/ None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1140,7 +1142,7 @@ impl App {
|
||||
}
|
||||
|
||||
self.chat_widget
|
||||
.add_info_message(format!("Opened {url} in your browser."), None);
|
||||
.add_info_message(format!("Opened {url} in your browser."), /*hint*/ None);
|
||||
}
|
||||
|
||||
fn clear_ui_header_lines_with_version(
|
||||
@@ -1256,7 +1258,7 @@ impl App {
|
||||
if self.active_thread_id.is_some() {
|
||||
return;
|
||||
}
|
||||
self.set_thread_active(thread_id, true).await;
|
||||
self.set_thread_active(thread_id, /*active*/ true).await;
|
||||
let receiver = if let Some(channel) = self.thread_event_channels.get_mut(&thread_id) {
|
||||
channel.receiver.take()
|
||||
} else {
|
||||
@@ -1297,7 +1299,7 @@ impl App {
|
||||
|
||||
async fn clear_active_thread(&mut self) {
|
||||
if let Some(active_id) = self.active_thread_id.take() {
|
||||
self.set_thread_active(active_id, false).await;
|
||||
self.set_thread_active(active_id, /*active*/ false).await;
|
||||
}
|
||||
self.active_thread_rx = None;
|
||||
self.refresh_pending_thread_approvals().await;
|
||||
@@ -1578,7 +1580,10 @@ impl App {
|
||||
let thread_id = session.session_id;
|
||||
self.primary_thread_id = Some(thread_id);
|
||||
self.primary_session_configured = Some(session.clone());
|
||||
self.upsert_agent_picker_thread(thread_id, None, None, false);
|
||||
self.upsert_agent_picker_thread(
|
||||
thread_id, /*agent_nickname*/ None, /*agent_role*/ None,
|
||||
/*is_closed*/ false,
|
||||
);
|
||||
self.ensure_thread_channel(thread_id);
|
||||
self.activate_thread_channel(thread_id).await;
|
||||
self.enqueue_thread_event(thread_id, event).await?;
|
||||
@@ -1609,7 +1614,7 @@ impl App {
|
||||
thread_id,
|
||||
session_source.get_nickname(),
|
||||
session_source.get_agent_role(),
|
||||
false,
|
||||
/*is_closed*/ false,
|
||||
);
|
||||
}
|
||||
Err(_) => {
|
||||
@@ -1628,7 +1633,7 @@ impl App {
|
||||
|
||||
if self.agent_navigation.is_empty() {
|
||||
self.chat_widget
|
||||
.add_info_message("No agents available yet.".to_string(), None);
|
||||
.add_info_message("No agents available yet.".to_string(), /*hint*/ None);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1751,7 +1756,7 @@ impl App {
|
||||
if is_replay_only {
|
||||
self.chat_widget.add_info_message(
|
||||
format!("Agent thread {thread_id} is closed. Replaying saved transcript."),
|
||||
None,
|
||||
/*hint*/ None,
|
||||
);
|
||||
}
|
||||
self.drain_active_thread_events(tui).await?;
|
||||
@@ -1904,13 +1909,15 @@ impl App {
|
||||
if let Some(event) = snapshot.session_configured {
|
||||
self.handle_codex_event_replay(event);
|
||||
}
|
||||
self.chat_widget.set_queue_autosend_suppressed(true);
|
||||
self.chat_widget
|
||||
.set_queue_autosend_suppressed(/*suppressed*/ true);
|
||||
self.chat_widget
|
||||
.restore_thread_input_state(snapshot.input_state);
|
||||
for event in snapshot.events {
|
||||
self.handle_codex_event_replay(event);
|
||||
}
|
||||
self.chat_widget.set_queue_autosend_suppressed(false);
|
||||
self.chat_widget
|
||||
.set_queue_autosend_suppressed(/*suppressed*/ false);
|
||||
if resume_restored_queue {
|
||||
self.chat_widget.maybe_send_next_queued_input();
|
||||
}
|
||||
@@ -2025,7 +2032,7 @@ impl App {
|
||||
.as_ref()
|
||||
.is_some_and(|cmd| !cmd.is_empty())
|
||||
{
|
||||
session_telemetry.counter("codex.status_line", 1, &[]);
|
||||
session_telemetry.counter("codex.status_line", /*inc*/ 1, &[]);
|
||||
}
|
||||
|
||||
let status_line_invalid_items_warned = Arc::new(AtomicBool::new(false));
|
||||
@@ -2067,7 +2074,7 @@ impl App {
|
||||
config.clone(),
|
||||
target_session.path.clone(),
|
||||
auth_manager.clone(),
|
||||
None,
|
||||
/*parent_trace*/ None,
|
||||
)
|
||||
.await
|
||||
.wrap_err_with(|| {
|
||||
@@ -2098,14 +2105,18 @@ impl App {
|
||||
ChatWidget::new_from_existing(init, resumed.thread, resumed.session_configured)
|
||||
}
|
||||
SessionSelection::Fork(target_session) => {
|
||||
session_telemetry.counter("codex.thread.fork", 1, &[("source", "cli_subcommand")]);
|
||||
session_telemetry.counter(
|
||||
"codex.thread.fork",
|
||||
/*inc*/ 1,
|
||||
&[("source", "cli_subcommand")],
|
||||
);
|
||||
let forked = thread_manager
|
||||
.fork_thread(
|
||||
usize::MAX,
|
||||
config.clone(),
|
||||
target_session.path.clone(),
|
||||
false,
|
||||
None,
|
||||
/*persist_extended_history*/ false,
|
||||
/*parent_trace*/ None,
|
||||
)
|
||||
.await
|
||||
.wrap_err_with(|| {
|
||||
@@ -2406,13 +2417,19 @@ impl App {
|
||||
self.start_fresh_session_with_summary_hint(tui).await;
|
||||
}
|
||||
AppEvent::ClearUi => {
|
||||
self.clear_terminal_ui(tui, false)?;
|
||||
self.clear_terminal_ui(tui, /*redraw_header*/ false)?;
|
||||
self.reset_app_ui_state_after_clear();
|
||||
|
||||
self.start_fresh_session_with_summary_hint(tui).await;
|
||||
}
|
||||
AppEvent::OpenResumePicker => {
|
||||
match crate::resume_picker::run_resume_picker(tui, &self.config, false).await? {
|
||||
match crate::resume_picker::run_resume_picker(
|
||||
tui,
|
||||
&self.config,
|
||||
/*show_all*/ false,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
SessionSelection::Resume(target_session) => {
|
||||
let current_cwd = self.config.cwd.clone();
|
||||
let resume_cwd = match crate::resolve_cwd_for_resume_or_fork(
|
||||
@@ -2422,7 +2439,7 @@ impl App {
|
||||
target_session.thread_id,
|
||||
&target_session.path,
|
||||
CwdPromptAction::Resume,
|
||||
true,
|
||||
/*allow_prompt*/ true,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
@@ -2456,7 +2473,7 @@ impl App {
|
||||
resume_config.clone(),
|
||||
target_session.path.clone(),
|
||||
self.auth_manager.clone(),
|
||||
None,
|
||||
/*parent_trace*/ None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -2507,7 +2524,7 @@ impl App {
|
||||
AppEvent::ForkCurrentSession => {
|
||||
self.session_telemetry.counter(
|
||||
"codex.thread.fork",
|
||||
1,
|
||||
/*inc*/ 1,
|
||||
&[("source", "slash_command")],
|
||||
);
|
||||
let summary = session_summary(
|
||||
@@ -2525,7 +2542,13 @@ impl App {
|
||||
if path.exists() {
|
||||
match self
|
||||
.server
|
||||
.fork_thread(usize::MAX, self.config.clone(), path.clone(), false, None)
|
||||
.fork_thread(
|
||||
usize::MAX,
|
||||
self.config.clone(),
|
||||
path.clone(),
|
||||
/*persist_extended_history*/ false,
|
||||
/*parent_trace*/ None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(forked) => {
|
||||
@@ -2777,7 +2800,7 @@ impl App {
|
||||
AppEvent::OpenWindowsSandboxFallbackPrompt { preset } => {
|
||||
self.session_telemetry.counter(
|
||||
"codex.windows_sandbox.fallback_prompt_shown",
|
||||
1,
|
||||
/*inc*/ 1,
|
||||
&[],
|
||||
);
|
||||
self.chat_widget.clear_windows_sandbox_setup_status();
|
||||
@@ -2971,7 +2994,7 @@ impl App {
|
||||
self.chat_widget
|
||||
.add_to_history(history_cell::new_info_event(
|
||||
format!("Sandbox read access granted for {}", path.display()),
|
||||
None,
|
||||
/*hint*/ None,
|
||||
));
|
||||
}
|
||||
},
|
||||
@@ -3106,7 +3129,7 @@ impl App {
|
||||
message.push_str(profile);
|
||||
message.push_str(" profile");
|
||||
}
|
||||
self.chat_widget.add_info_message(message, None);
|
||||
self.chat_widget.add_info_message(message, /*hint*/ None);
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!(
|
||||
@@ -3140,7 +3163,7 @@ impl App {
|
||||
message.push_str(profile);
|
||||
message.push_str(" profile");
|
||||
}
|
||||
self.chat_widget.add_info_message(message, None);
|
||||
self.chat_widget.add_info_message(message, /*hint*/ None);
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!(
|
||||
@@ -3176,7 +3199,7 @@ impl App {
|
||||
message.push_str(profile);
|
||||
message.push_str(" profile");
|
||||
}
|
||||
self.chat_widget.add_info_message(message, None);
|
||||
self.chat_widget.add_info_message(message, /*hint*/ None);
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!(error = %err, "failed to persist fast mode selection");
|
||||
@@ -3223,7 +3246,7 @@ impl App {
|
||||
let selection = name.unwrap_or_else(|| "System default".to_string());
|
||||
self.chat_widget.add_info_message(
|
||||
format!("Realtime {} set to {selection}", kind.noun()),
|
||||
None,
|
||||
/*hint*/ None,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3369,7 +3392,7 @@ impl App {
|
||||
}
|
||||
AppEvent::PersistFullAccessWarningAcknowledged => {
|
||||
if let Err(err) = ConfigEditsBuilder::new(&self.config.codex_home)
|
||||
.set_hide_full_access_warning(true)
|
||||
.set_hide_full_access_warning(/*acknowledged*/ true)
|
||||
.apply()
|
||||
.await
|
||||
{
|
||||
@@ -3384,7 +3407,7 @@ impl App {
|
||||
}
|
||||
AppEvent::PersistWorldWritableWarningAcknowledged => {
|
||||
if let Err(err) = ConfigEditsBuilder::new(&self.config.codex_home)
|
||||
.set_hide_world_writable_warning(true)
|
||||
.set_hide_world_writable_warning(/*acknowledged*/ true)
|
||||
.apply()
|
||||
.await
|
||||
{
|
||||
@@ -3399,7 +3422,7 @@ impl App {
|
||||
}
|
||||
AppEvent::PersistRateLimitSwitchPromptHidden => {
|
||||
if let Err(err) = ConfigEditsBuilder::new(&self.config.codex_home)
|
||||
.set_hide_rate_limit_model_nudge(true)
|
||||
.set_hide_rate_limit_model_nudge(/*acknowledged*/ true)
|
||||
.apply()
|
||||
.await
|
||||
{
|
||||
@@ -3795,7 +3818,7 @@ impl App {
|
||||
format!(
|
||||
"Agent thread {closed_thread_id} closed. Switched back to main thread."
|
||||
),
|
||||
None,
|
||||
/*hint*/ None,
|
||||
);
|
||||
} else {
|
||||
self.clear_active_thread().await;
|
||||
@@ -3834,7 +3857,7 @@ impl App {
|
||||
thread_id,
|
||||
config_snapshot.session_source.get_nickname(),
|
||||
config_snapshot.session_source.get_agent_role(),
|
||||
false,
|
||||
/*is_closed*/ false,
|
||||
);
|
||||
let event = Event {
|
||||
id: String::new(),
|
||||
@@ -4002,7 +4025,7 @@ impl App {
|
||||
fn reset_external_editor_state(&mut self, tui: &mut tui::Tui) {
|
||||
self.chat_widget
|
||||
.set_external_editor_state(ExternalEditorState::Closed);
|
||||
self.chat_widget.set_footer_hint_override(None);
|
||||
self.chat_widget.set_footer_hint_override(/*items*/ None);
|
||||
tui.frame_requester().schedule_frame();
|
||||
}
|
||||
|
||||
@@ -4066,7 +4089,7 @@ impl App {
|
||||
if !self.chat_widget.can_run_ctrl_l_clear_now() {
|
||||
return;
|
||||
}
|
||||
if let Err(err) = self.clear_terminal_ui(tui, false) {
|
||||
if let Err(err) = self.clear_terminal_ui(tui, /*redraw_header*/ false) {
|
||||
tracing::warn!(error = %err, "failed to clear terminal UI");
|
||||
self.chat_widget
|
||||
.add_error_message(format!("Failed to clear terminal UI: {err}"));
|
||||
|
||||
@@ -106,7 +106,10 @@ impl AgentNavigationState {
|
||||
if let Some(entry) = self.threads.get_mut(&thread_id) {
|
||||
entry.is_closed = true;
|
||||
} else {
|
||||
self.upsert(thread_id, None, None, true);
|
||||
self.upsert(
|
||||
thread_id, /*agent_nickname*/ None, /*agent_role*/ None,
|
||||
/*is_closed*/ true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +205,11 @@ impl AgentNavigationState {
|
||||
is_primary,
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| format_agent_picker_item_name(None, None, is_primary)),
|
||||
.unwrap_or_else(|| {
|
||||
format_agent_picker_item_name(
|
||||
/*agent_nickname*/ None, /*agent_role*/ None, is_primary,
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ impl App {
|
||||
} else {
|
||||
self.backtrack.nth_user_message = usize::MAX;
|
||||
if let Some(Overlay::Transcript(t)) = &mut self.overlay {
|
||||
t.set_highlight_cell(None);
|
||||
t.set_highlight_cell(/*cell*/ None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ pub(crate) struct AsciiAnimation {
|
||||
|
||||
impl AsciiAnimation {
|
||||
pub(crate) fn new(request_frame: FrameRequester) -> Self {
|
||||
Self::with_variants(request_frame, ALL_VARIANTS, 0)
|
||||
Self::with_variants(request_frame, ALL_VARIANTS, /*variant_idx*/ 0)
|
||||
}
|
||||
|
||||
pub(crate) fn with_variants(
|
||||
|
||||
@@ -506,7 +506,7 @@ impl crate::render::renderable::Renderable for AppLinkView {
|
||||
])
|
||||
.areas(area);
|
||||
|
||||
let inner = content_area.inset(Insets::vh(1, 2));
|
||||
let inner = content_area.inset(Insets::vh(/*v*/ 1, /*h*/ 2));
|
||||
let content_width = inner.width.max(1);
|
||||
let lines = self.content_lines(content_width);
|
||||
Paragraph::new(lines)
|
||||
|
||||
@@ -674,7 +674,12 @@ impl ChatComposer {
|
||||
};
|
||||
let [composer_rect, popup_rect] =
|
||||
Layout::vertical([Constraint::Min(3), popup_constraint]).areas(area);
|
||||
let mut textarea_rect = composer_rect.inset(Insets::tlbr(1, LIVE_PREFIX_COLS, 1, 1));
|
||||
let mut textarea_rect = composer_rect.inset(Insets::tlbr(
|
||||
/*top*/ 1,
|
||||
LIVE_PREFIX_COLS,
|
||||
/*bottom*/ 1,
|
||||
/*right*/ 1,
|
||||
));
|
||||
let remote_images_height = self
|
||||
.remote_images_lines(textarea_rect.width)
|
||||
.len()
|
||||
@@ -1037,7 +1042,7 @@ impl ChatComposer {
|
||||
self.bind_mentions_from_snapshot(mention_bindings);
|
||||
self.relabel_attached_images_and_update_placeholders();
|
||||
self.selected_remote_image_index = None;
|
||||
self.textarea.set_cursor(0);
|
||||
self.textarea.set_cursor(/*pos*/ 0);
|
||||
self.sync_popups();
|
||||
}
|
||||
|
||||
@@ -2092,14 +2097,14 @@ impl ChatComposer {
|
||||
///
|
||||
/// The returned string **does not** include the leading `@`.
|
||||
fn current_at_token(textarea: &TextArea) -> Option<String> {
|
||||
Self::current_prefixed_token(textarea, '@', false)
|
||||
Self::current_prefixed_token(textarea, '@', /*allow_empty*/ false)
|
||||
}
|
||||
|
||||
fn current_mention_token(&self) -> Option<String> {
|
||||
if !self.mentions_enabled() {
|
||||
return None;
|
||||
}
|
||||
Self::current_prefixed_token(&self.textarea, '$', true)
|
||||
Self::current_prefixed_token(&self.textarea, '$', /*allow_empty*/ true)
|
||||
}
|
||||
|
||||
/// Replace the active `@token` (the one under the cursor) with `path`.
|
||||
@@ -2329,7 +2334,7 @@ impl ChatComposer {
|
||||
r#"Unrecognized command '/{name}'. Type "/" for a list of supported commands."#
|
||||
);
|
||||
self.app_event_tx.send(AppEvent::InsertHistoryCell(Box::new(
|
||||
history_cell::new_info_event(message, None),
|
||||
history_cell::new_info_event(message, /*hint*/ None),
|
||||
)));
|
||||
self.set_text_content_with_mention_bindings(
|
||||
original_input.clone(),
|
||||
@@ -2481,7 +2486,9 @@ impl ChatComposer {
|
||||
return (result, true);
|
||||
}
|
||||
|
||||
if let Some((text, text_elements)) = self.prepare_submission_text(true) {
|
||||
if let Some((text, text_elements)) =
|
||||
self.prepare_submission_text(/*record_history*/ true)
|
||||
{
|
||||
if should_queue {
|
||||
(
|
||||
InputResult::Queued {
|
||||
@@ -2788,7 +2795,7 @@ impl ChatComposer {
|
||||
code: KeyCode::Enter,
|
||||
modifiers: KeyModifiers::NONE,
|
||||
..
|
||||
} => self.handle_submission(false),
|
||||
} => self.handle_submission(/*should_queue*/ false),
|
||||
input => self.handle_input_basic(input),
|
||||
}
|
||||
}
|
||||
@@ -4160,7 +4167,7 @@ impl Renderable for ChatComposer {
|
||||
}
|
||||
|
||||
fn render(&self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_with_mask(area, buf, None);
|
||||
self.render_with_mask(area, buf, /*mask_char*/ None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4254,7 +4261,10 @@ impl ChatComposer {
|
||||
let right_line = if status_line_active {
|
||||
let full =
|
||||
mode_indicator_line(self.collaboration_mode_indicator, show_cycle_hint);
|
||||
let compact = mode_indicator_line(self.collaboration_mode_indicator, false);
|
||||
let compact = mode_indicator_line(
|
||||
self.collaboration_mode_indicator,
|
||||
/*show_cycle_hint*/ false,
|
||||
);
|
||||
let full_width = full.as_ref().map(|l| l.width() as u16).unwrap_or(0);
|
||||
if can_show_left_with_context(hint_rect, left_width, full_width) {
|
||||
full
|
||||
|
||||
@@ -274,7 +274,9 @@ impl WidgetRef for CommandPopup {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
let rows = self.rows_from_matches(self.filtered());
|
||||
render_rows(
|
||||
area.inset(Insets::tlbr(0, 2, 0, 0)),
|
||||
area.inset(Insets::tlbr(
|
||||
/*top*/ 0, /*left*/ 2, /*bottom*/ 0, /*right*/ 0,
|
||||
)),
|
||||
buf,
|
||||
&rows,
|
||||
&self.state,
|
||||
|
||||
@@ -243,7 +243,7 @@ impl Renderable for ExperimentalFeaturesView {
|
||||
Constraint::Max(1),
|
||||
Constraint::Length(rows_height),
|
||||
])
|
||||
.areas(content_area.inset(Insets::vh(1, 2)));
|
||||
.areas(content_area.inset(Insets::vh(/*v*/ 1, /*h*/ 2)));
|
||||
|
||||
self.header.render(header_area, buf);
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ impl FeedbackNoteView {
|
||||
self.include_logs,
|
||||
&attachment_paths,
|
||||
Some(SessionSource::Cli),
|
||||
None,
|
||||
/*logs_override*/ None,
|
||||
);
|
||||
|
||||
match result {
|
||||
|
||||
@@ -141,7 +141,9 @@ impl WidgetRef for &FileSearchPopup {
|
||||
};
|
||||
|
||||
render_rows(
|
||||
area.inset(Insets::tlbr(0, 2, 0, 0)),
|
||||
area.inset(Insets::tlbr(
|
||||
/*top*/ 0, /*left*/ 2, /*bottom*/ 0, /*right*/ 0,
|
||||
)),
|
||||
buf,
|
||||
&rows_all,
|
||||
&self.state,
|
||||
|
||||
@@ -199,7 +199,14 @@ pub(crate) fn footer_height(props: &FooterProps) -> u16 {
|
||||
| FooterMode::ShortcutOverlay
|
||||
| FooterMode::EscHint => false,
|
||||
};
|
||||
footer_from_props_lines(props, None, false, show_shortcuts_hint, show_queue_hint).len() as u16
|
||||
footer_from_props_lines(
|
||||
props,
|
||||
/*collaboration_mode_indicator*/ None,
|
||||
/*show_cycle_hint*/ false,
|
||||
show_shortcuts_hint,
|
||||
show_queue_hint,
|
||||
)
|
||||
.len() as u16
|
||||
}
|
||||
|
||||
/// Render a single precomputed footer line.
|
||||
|
||||
@@ -1104,24 +1104,25 @@ impl McpServerElicitationOverlay {
|
||||
}
|
||||
self.validation_error = None;
|
||||
if self.request.response_mode == McpServerElicitationResponseMode::ApprovalAction {
|
||||
let (decision, meta) = match self.field_value(0).as_ref().and_then(Value::as_str) {
|
||||
Some(APPROVAL_ACCEPT_ONCE_VALUE) => (ElicitationAction::Accept, None),
|
||||
Some(APPROVAL_ACCEPT_SESSION_VALUE) => (
|
||||
ElicitationAction::Accept,
|
||||
Some(serde_json::json!({
|
||||
APPROVAL_PERSIST_KEY: APPROVAL_PERSIST_SESSION_VALUE,
|
||||
})),
|
||||
),
|
||||
Some(APPROVAL_ACCEPT_ALWAYS_VALUE) => (
|
||||
ElicitationAction::Accept,
|
||||
Some(serde_json::json!({
|
||||
APPROVAL_PERSIST_KEY: APPROVAL_PERSIST_ALWAYS_VALUE,
|
||||
})),
|
||||
),
|
||||
Some(APPROVAL_DECLINE_VALUE) => (ElicitationAction::Decline, None),
|
||||
Some(APPROVAL_CANCEL_VALUE) => (ElicitationAction::Cancel, None),
|
||||
_ => (ElicitationAction::Cancel, None),
|
||||
};
|
||||
let (decision, meta) =
|
||||
match self.field_value(/*idx*/ 0).as_ref().and_then(Value::as_str) {
|
||||
Some(APPROVAL_ACCEPT_ONCE_VALUE) => (ElicitationAction::Accept, None),
|
||||
Some(APPROVAL_ACCEPT_SESSION_VALUE) => (
|
||||
ElicitationAction::Accept,
|
||||
Some(serde_json::json!({
|
||||
APPROVAL_PERSIST_KEY: APPROVAL_PERSIST_SESSION_VALUE,
|
||||
})),
|
||||
),
|
||||
Some(APPROVAL_ACCEPT_ALWAYS_VALUE) => (
|
||||
ElicitationAction::Accept,
|
||||
Some(serde_json::json!({
|
||||
APPROVAL_PERSIST_KEY: APPROVAL_PERSIST_ALWAYS_VALUE,
|
||||
})),
|
||||
),
|
||||
Some(APPROVAL_DECLINE_VALUE) => (ElicitationAction::Decline, None),
|
||||
Some(APPROVAL_CANCEL_VALUE) => (ElicitationAction::Cancel, None),
|
||||
_ => (ElicitationAction::Cancel, None),
|
||||
};
|
||||
self.app_event_tx.send(AppEvent::SubmitThreadOp {
|
||||
thread_id: self.request.thread_id,
|
||||
op: Op::ResolveElicitation {
|
||||
@@ -1171,7 +1172,7 @@ impl McpServerElicitationOverlay {
|
||||
if self.current_index() + 1 >= self.field_count() {
|
||||
self.submit_answers();
|
||||
} else {
|
||||
self.move_field(true);
|
||||
self.move_field(/*next*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1466,7 +1467,7 @@ impl BottomPaneView for McpServerElicitationOverlay {
|
||||
modifiers: KeyModifiers::NONE,
|
||||
..
|
||||
} => {
|
||||
self.move_field(false);
|
||||
self.move_field(/*next*/ false);
|
||||
return;
|
||||
}
|
||||
KeyEvent {
|
||||
@@ -1479,7 +1480,7 @@ impl BottomPaneView for McpServerElicitationOverlay {
|
||||
modifiers: KeyModifiers::NONE,
|
||||
..
|
||||
} => {
|
||||
self.move_field(true);
|
||||
self.move_field(/*next*/ true);
|
||||
return;
|
||||
}
|
||||
KeyEvent {
|
||||
@@ -1487,7 +1488,7 @@ impl BottomPaneView for McpServerElicitationOverlay {
|
||||
modifiers: KeyModifiers::NONE,
|
||||
..
|
||||
} if self.current_field_is_select() => {
|
||||
self.move_field(false);
|
||||
self.move_field(/*next*/ false);
|
||||
return;
|
||||
}
|
||||
KeyEvent {
|
||||
@@ -1495,7 +1496,7 @@ impl BottomPaneView for McpServerElicitationOverlay {
|
||||
modifiers: KeyModifiers::NONE,
|
||||
..
|
||||
} if self.current_field_is_select() => {
|
||||
self.move_field(true);
|
||||
self.move_field(/*next*/ true);
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
@@ -1518,10 +1519,10 @@ impl BottomPaneView for McpServerElicitationOverlay {
|
||||
}
|
||||
}
|
||||
KeyCode::Backspace | KeyCode::Delete => self.clear_selection(),
|
||||
KeyCode::Char(' ') => self.select_current_option(true),
|
||||
KeyCode::Char(' ') => self.select_current_option(/*committed*/ true),
|
||||
KeyCode::Enter => {
|
||||
if self.selected_option_index().is_some() {
|
||||
self.select_current_option(true);
|
||||
self.select_current_option(/*committed*/ true);
|
||||
}
|
||||
self.go_next_or_submit();
|
||||
}
|
||||
@@ -1530,7 +1531,7 @@ impl BottomPaneView for McpServerElicitationOverlay {
|
||||
if let Some(answer) = self.current_answer_mut() {
|
||||
answer.selection.selected_idx = Some(option_idx);
|
||||
}
|
||||
self.select_current_option(true);
|
||||
self.select_current_option(/*committed*/ true);
|
||||
self.go_next_or_submit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,14 +700,14 @@ impl BottomPane {
|
||||
|
||||
pub(crate) fn show_esc_backtrack_hint(&mut self) {
|
||||
self.esc_backtrack_hint = true;
|
||||
self.composer.set_esc_backtrack_hint(true);
|
||||
self.composer.set_esc_backtrack_hint(/*show*/ true);
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
pub(crate) fn clear_esc_backtrack_hint(&mut self) {
|
||||
if self.esc_backtrack_hint {
|
||||
self.esc_backtrack_hint = false;
|
||||
self.composer.set_esc_backtrack_hint(false);
|
||||
self.composer.set_esc_backtrack_hint(/*show*/ false);
|
||||
self.request_redraw();
|
||||
}
|
||||
}
|
||||
@@ -729,7 +729,7 @@ impl BottomPane {
|
||||
));
|
||||
}
|
||||
if let Some(status) = self.status.as_mut() {
|
||||
status.set_interrupt_hint_visible(true);
|
||||
status.set_interrupt_hint_visible(/*visible*/ true);
|
||||
}
|
||||
self.sync_status_inline_message();
|
||||
self.request_redraw();
|
||||
@@ -943,7 +943,7 @@ impl BottomPane {
|
||||
);
|
||||
self.pause_status_timer_for_modal();
|
||||
self.set_composer_input_enabled(
|
||||
false,
|
||||
/*enabled*/ false,
|
||||
Some("Answer the questions to continue.".to_string()),
|
||||
);
|
||||
self.push_view(Box::new(modal));
|
||||
@@ -1004,7 +1004,7 @@ impl BottomPane {
|
||||
);
|
||||
self.pause_status_timer_for_modal();
|
||||
self.set_composer_input_enabled(
|
||||
false,
|
||||
/*enabled*/ false,
|
||||
Some("Respond to the tool suggestion to continue.".to_string()),
|
||||
);
|
||||
self.push_view(Box::new(view));
|
||||
@@ -1020,7 +1020,7 @@ impl BottomPane {
|
||||
);
|
||||
self.pause_status_timer_for_modal();
|
||||
self.set_composer_input_enabled(
|
||||
false,
|
||||
/*enabled*/ false,
|
||||
Some("Respond to the MCP server request to continue.".to_string()),
|
||||
);
|
||||
self.push_view(Box::new(modal));
|
||||
@@ -1028,7 +1028,7 @@ impl BottomPane {
|
||||
|
||||
fn on_active_view_complete(&mut self) {
|
||||
self.resume_status_timer_after_modal();
|
||||
self.set_composer_input_enabled(true, None);
|
||||
self.set_composer_input_enabled(/*enabled*/ true, /*placeholder*/ None);
|
||||
}
|
||||
|
||||
fn pause_status_timer_for_modal(&mut self) {
|
||||
@@ -1131,12 +1131,15 @@ impl BottomPane {
|
||||
} else {
|
||||
let mut flex = FlexRenderable::new();
|
||||
if let Some(status) = &self.status {
|
||||
flex.push(0, RenderableItem::Borrowed(status));
|
||||
flex.push(/*flex*/ 0, RenderableItem::Borrowed(status));
|
||||
}
|
||||
// Avoid double-surfacing the same summary and avoid adding an extra
|
||||
// row while the status line is already visible.
|
||||
if self.status.is_none() && !self.unified_exec_footer.is_empty() {
|
||||
flex.push(0, RenderableItem::Borrowed(&self.unified_exec_footer));
|
||||
flex.push(
|
||||
/*flex*/ 0,
|
||||
RenderableItem::Borrowed(&self.unified_exec_footer),
|
||||
);
|
||||
}
|
||||
let has_pending_thread_approvals = !self.pending_thread_approvals.is_empty();
|
||||
let has_pending_input = !self.pending_input_preview.queued_messages.is_empty()
|
||||
@@ -1145,19 +1148,25 @@ impl BottomPane {
|
||||
self.status.is_some() || !self.unified_exec_footer.is_empty();
|
||||
let has_inline_previews = has_pending_thread_approvals || has_pending_input;
|
||||
if has_inline_previews && has_status_or_footer {
|
||||
flex.push(0, RenderableItem::Owned("".into()));
|
||||
flex.push(/*flex*/ 0, RenderableItem::Owned("".into()));
|
||||
}
|
||||
flex.push(1, RenderableItem::Borrowed(&self.pending_thread_approvals));
|
||||
flex.push(
|
||||
/*flex*/ 1,
|
||||
RenderableItem::Borrowed(&self.pending_thread_approvals),
|
||||
);
|
||||
if has_pending_thread_approvals && has_pending_input {
|
||||
flex.push(0, RenderableItem::Owned("".into()));
|
||||
flex.push(/*flex*/ 0, RenderableItem::Owned("".into()));
|
||||
}
|
||||
flex.push(1, RenderableItem::Borrowed(&self.pending_input_preview));
|
||||
flex.push(
|
||||
/*flex*/ 1,
|
||||
RenderableItem::Borrowed(&self.pending_input_preview),
|
||||
);
|
||||
if !has_inline_previews && has_status_or_footer {
|
||||
flex.push(0, RenderableItem::Owned("".into()));
|
||||
flex.push(/*flex*/ 0, RenderableItem::Owned("".into()));
|
||||
}
|
||||
let mut flex2 = FlexRenderable::new();
|
||||
flex2.push(1, RenderableItem::Owned(flex.into()));
|
||||
flex2.push(0, RenderableItem::Borrowed(&self.composer));
|
||||
flex2.push(/*flex*/ 1, RenderableItem::Owned(flex.into()));
|
||||
flex2.push(/*flex*/ 0, RenderableItem::Borrowed(&self.composer));
|
||||
RenderableItem::Owned(Box::new(flex2))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,7 +533,7 @@ impl Renderable for MultiSelectPicker {
|
||||
Constraint::Length(2),
|
||||
Constraint::Length(rows_height),
|
||||
])
|
||||
.areas(content_area.inset(Insets::vh(1, 2)));
|
||||
.areas(content_area.inset(Insets::vh(/*v*/ 1, /*h*/ 2)));
|
||||
|
||||
self.header.render(header_area, buf);
|
||||
|
||||
|
||||
@@ -706,7 +706,7 @@ impl RequestUserInputOverlay {
|
||||
self.submit_answers();
|
||||
}
|
||||
} else {
|
||||
self.move_question(true);
|
||||
self.move_question(/*next*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -958,10 +958,10 @@ impl RequestUserInputOverlay {
|
||||
}
|
||||
}
|
||||
KeyCode::Up | KeyCode::Char('k') => {
|
||||
state.move_up_wrap(2);
|
||||
state.move_up_wrap(/*len*/ 2);
|
||||
}
|
||||
KeyCode::Down | KeyCode::Char('j') => {
|
||||
state.move_down_wrap(2);
|
||||
state.move_down_wrap(/*len*/ 2);
|
||||
}
|
||||
KeyCode::Enter => {
|
||||
let selected = state.selected_idx.unwrap_or(0);
|
||||
@@ -1024,7 +1024,7 @@ impl BottomPaneView for RequestUserInputOverlay {
|
||||
modifiers: KeyModifiers::NONE,
|
||||
..
|
||||
} => {
|
||||
self.move_question(false);
|
||||
self.move_question(/*next*/ false);
|
||||
return;
|
||||
}
|
||||
KeyEvent {
|
||||
@@ -1037,7 +1037,7 @@ impl BottomPaneView for RequestUserInputOverlay {
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => {
|
||||
self.move_question(true);
|
||||
self.move_question(/*next*/ true);
|
||||
return;
|
||||
}
|
||||
KeyEvent {
|
||||
@@ -1045,7 +1045,7 @@ impl BottomPaneView for RequestUserInputOverlay {
|
||||
modifiers: KeyModifiers::NONE,
|
||||
..
|
||||
} if self.has_options() && matches!(self.focus, Focus::Options) => {
|
||||
self.move_question(false);
|
||||
self.move_question(/*next*/ false);
|
||||
return;
|
||||
}
|
||||
KeyEvent {
|
||||
@@ -1053,7 +1053,7 @@ impl BottomPaneView for RequestUserInputOverlay {
|
||||
modifiers: KeyModifiers::NONE,
|
||||
..
|
||||
} if self.has_options() && matches!(self.focus, Focus::Options) => {
|
||||
self.move_question(false);
|
||||
self.move_question(/*next*/ false);
|
||||
return;
|
||||
}
|
||||
KeyEvent {
|
||||
@@ -1061,7 +1061,7 @@ impl BottomPaneView for RequestUserInputOverlay {
|
||||
modifiers: KeyModifiers::NONE,
|
||||
..
|
||||
} if self.has_options() && matches!(self.focus, Focus::Options) => {
|
||||
self.move_question(true);
|
||||
self.move_question(/*next*/ true);
|
||||
return;
|
||||
}
|
||||
KeyEvent {
|
||||
@@ -1069,7 +1069,7 @@ impl BottomPaneView for RequestUserInputOverlay {
|
||||
modifiers: KeyModifiers::NONE,
|
||||
..
|
||||
} if self.has_options() && matches!(self.focus, Focus::Options) => {
|
||||
self.move_question(true);
|
||||
self.move_question(/*next*/ true);
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
@@ -1105,7 +1105,7 @@ impl BottomPaneView for RequestUserInputOverlay {
|
||||
}
|
||||
}
|
||||
KeyCode::Char(' ') => {
|
||||
self.select_current_option(true);
|
||||
self.select_current_option(/*committed*/ true);
|
||||
}
|
||||
KeyCode::Backspace | KeyCode::Delete => {
|
||||
self.clear_selection();
|
||||
@@ -1119,7 +1119,7 @@ impl BottomPaneView for RequestUserInputOverlay {
|
||||
KeyCode::Enter => {
|
||||
let has_selection = self.selected_option_index().is_some();
|
||||
if has_selection {
|
||||
self.select_current_option(true);
|
||||
self.select_current_option(/*committed*/ true);
|
||||
}
|
||||
self.go_next_or_submit();
|
||||
}
|
||||
@@ -1128,7 +1128,7 @@ impl BottomPaneView for RequestUserInputOverlay {
|
||||
if let Some(answer) = self.current_answer_mut() {
|
||||
answer.options_state.selected_idx = Some(option_idx);
|
||||
}
|
||||
self.select_current_option(true);
|
||||
self.select_current_option(/*committed*/ true);
|
||||
self.go_next_or_submit();
|
||||
}
|
||||
}
|
||||
@@ -1158,7 +1158,7 @@ impl BottomPaneView for RequestUserInputOverlay {
|
||||
if !self.handle_composer_input_result(result) {
|
||||
self.pending_submission_draft = None;
|
||||
if self.has_options() {
|
||||
self.select_current_option(true);
|
||||
self.select_current_option(/*committed*/ true);
|
||||
}
|
||||
self.go_next_or_submit();
|
||||
}
|
||||
|
||||
@@ -199,7 +199,9 @@ impl WidgetRef for SkillPopup {
|
||||
};
|
||||
let rows = self.rows_from_matches(self.filtered());
|
||||
render_rows_single_line(
|
||||
list_area.inset(Insets::tlbr(0, 2, 0, 0)),
|
||||
list_area.inset(Insets::tlbr(
|
||||
/*top*/ 0, /*left*/ 2, /*bottom*/ 0, /*right*/ 0,
|
||||
)),
|
||||
buf,
|
||||
&rows,
|
||||
&self.state,
|
||||
|
||||
@@ -314,7 +314,7 @@ impl Renderable for SkillsToggleView {
|
||||
Constraint::Length(2),
|
||||
Constraint::Length(rows_height),
|
||||
])
|
||||
.areas(content_area.inset(Insets::vh(1, 2)));
|
||||
.areas(content_area.inset(Insets::vh(/*v*/ 1, /*h*/ 2)));
|
||||
|
||||
self.header.render(header_area, buf);
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ impl StatusLineSetupView {
|
||||
if !used_ids.insert(item_id.clone()) {
|
||||
continue;
|
||||
}
|
||||
items.push(Self::status_line_select_item(item, true));
|
||||
items.push(Self::status_line_select_item(item, /*enabled*/ true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ impl StatusLineSetupView {
|
||||
if used_ids.contains(&item_id) {
|
||||
continue;
|
||||
}
|
||||
items.push(Self::status_line_select_item(item, false));
|
||||
items.push(Self::status_line_select_item(item, /*enabled*/ false));
|
||||
}
|
||||
|
||||
Self {
|
||||
|
||||
@@ -101,7 +101,7 @@ impl TextArea {
|
||||
/// as submit or slash-command dispatch clear the draft through this method and still want
|
||||
/// `Ctrl+Y` to recover the user's most recent kill.
|
||||
pub fn set_text_clearing_elements(&mut self, text: &str) {
|
||||
self.set_text_inner(text, None);
|
||||
self.set_text_inner(text, /*elements*/ None);
|
||||
}
|
||||
|
||||
/// Replace the visible textarea text and rebuild the provided text elements.
|
||||
@@ -160,7 +160,7 @@ impl TextArea {
|
||||
if pos <= self.cursor_pos {
|
||||
self.cursor_pos += text.len();
|
||||
}
|
||||
self.shift_elements(pos, 0, text.len());
|
||||
self.shift_elements(pos, /*removed*/ 0, text.len());
|
||||
self.preferred_col = None;
|
||||
}
|
||||
|
||||
@@ -355,7 +355,7 @@ impl TextArea {
|
||||
code: KeyCode::Char('h'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => self.delete_backward(1),
|
||||
} => self.delete_backward(/*n*/ 1),
|
||||
KeyEvent {
|
||||
code: KeyCode::Delete,
|
||||
modifiers: KeyModifiers::ALT,
|
||||
@@ -374,7 +374,7 @@ impl TextArea {
|
||||
code: KeyCode::Char('d'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => self.delete_forward(1),
|
||||
} => self.delete_forward(/*n*/ 1),
|
||||
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('w'),
|
||||
@@ -507,27 +507,27 @@ impl TextArea {
|
||||
code: KeyCode::Home,
|
||||
..
|
||||
} => {
|
||||
self.move_cursor_to_beginning_of_line(false);
|
||||
self.move_cursor_to_beginning_of_line(/*move_up_at_bol*/ false);
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('a'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => {
|
||||
self.move_cursor_to_beginning_of_line(true);
|
||||
self.move_cursor_to_beginning_of_line(/*move_up_at_bol*/ true);
|
||||
}
|
||||
|
||||
KeyEvent {
|
||||
code: KeyCode::End, ..
|
||||
} => {
|
||||
self.move_cursor_to_end_of_line(false);
|
||||
self.move_cursor_to_end_of_line(/*move_down_at_eol*/ false);
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('e'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => {
|
||||
self.move_cursor_to_end_of_line(true);
|
||||
self.move_cursor_to_end_of_line(/*move_down_at_eol*/ true);
|
||||
}
|
||||
_o => {
|
||||
#[cfg(feature = "debug-logs")]
|
||||
@@ -999,7 +999,7 @@ impl TextArea {
|
||||
}
|
||||
|
||||
fn add_element(&mut self, range: Range<usize>) -> u64 {
|
||||
self.add_element_with_id(range, None)
|
||||
self.add_element_with_id(range, /*name*/ None)
|
||||
}
|
||||
|
||||
/// Mark an existing text range as an atomic element without changing the text.
|
||||
@@ -1228,7 +1228,7 @@ impl TextArea {
|
||||
}
|
||||
start = idx;
|
||||
}
|
||||
self.adjust_pos_out_of_elements(start, true)
|
||||
self.adjust_pos_out_of_elements(start, /*prefer_start*/ true)
|
||||
}
|
||||
|
||||
pub(crate) fn end_of_next_word(&self) -> usize {
|
||||
@@ -1249,7 +1249,7 @@ impl TextArea {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.adjust_pos_out_of_elements(end, false)
|
||||
self.adjust_pos_out_of_elements(end, /*prefer_start*/ false)
|
||||
}
|
||||
|
||||
fn adjust_pos_out_of_elements(&self, pos: usize, prefer_start: bool) -> usize {
|
||||
|
||||
+149
-86
@@ -1194,7 +1194,7 @@ impl ChatWidget {
|
||||
fn set_status_header(&mut self, header: String) {
|
||||
self.set_status(
|
||||
header,
|
||||
None,
|
||||
/*details*/ None,
|
||||
StatusDetailsCapitalization::CapitalizeFirst,
|
||||
STATUS_DETAILS_DEFAULT_MAX_LINES,
|
||||
);
|
||||
@@ -1251,7 +1251,7 @@ impl ChatWidget {
|
||||
let enabled = !items.is_empty();
|
||||
self.bottom_pane.set_status_line_enabled(enabled);
|
||||
if !enabled {
|
||||
self.set_status_line(None);
|
||||
self.set_status_line(/*status_line*/ None);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1356,7 +1356,7 @@ impl ChatWidget {
|
||||
fn on_session_configured(&mut self, event: codex_protocol::protocol::SessionConfiguredEvent) {
|
||||
self.bottom_pane
|
||||
.set_history_metadata(event.history_log_id, event.history_entry_count);
|
||||
self.set_skills(None);
|
||||
self.set_skills(/*skills*/ None);
|
||||
self.session_network_proxy = event.network_proxy.clone();
|
||||
self.thread_id = Some(event.session_id);
|
||||
self.thread_name = event.thread_name.clone();
|
||||
@@ -1393,7 +1393,7 @@ impl ChatWidget {
|
||||
self.current_collaboration_mode = self.current_collaboration_mode.with_updates(
|
||||
Some(model_for_header.clone()),
|
||||
Some(event.reasoning_effort),
|
||||
None,
|
||||
/*developer_instructions*/ None,
|
||||
);
|
||||
if let Some(mask) = self.active_collaboration_mask.as_mut() {
|
||||
mask.model = Some(model_for_header.clone());
|
||||
@@ -1692,7 +1692,8 @@ impl ChatWidget {
|
||||
|
||||
fn on_task_started(&mut self) {
|
||||
self.agent_turn_running = true;
|
||||
self.turn_sleep_inhibitor.set_turn_running(true);
|
||||
self.turn_sleep_inhibitor
|
||||
.set_turn_running(/*turn_running*/ true);
|
||||
self.saw_plan_update_this_turn = false;
|
||||
self.saw_plan_item_this_turn = false;
|
||||
self.plan_delta_buffer.clear();
|
||||
@@ -1707,7 +1708,8 @@ impl ChatWidget {
|
||||
self.update_task_running_state();
|
||||
self.retry_status_header = None;
|
||||
self.pending_status_indicator_restore = false;
|
||||
self.bottom_pane.set_interrupt_hint_visible(true);
|
||||
self.bottom_pane
|
||||
.set_interrupt_hint_visible(/*visible*/ true);
|
||||
self.set_status_header(String::from("Working"));
|
||||
self.full_reasoning_buffer.clear();
|
||||
self.reasoning_buffer.clear();
|
||||
@@ -1756,7 +1758,8 @@ impl ChatWidget {
|
||||
// Mark task stopped and request redraw now that all content is in history.
|
||||
self.pending_status_indicator_restore = false;
|
||||
self.agent_turn_running = false;
|
||||
self.turn_sleep_inhibitor.set_turn_running(false);
|
||||
self.turn_sleep_inhibitor
|
||||
.set_turn_running(/*turn_running*/ false);
|
||||
self.update_task_running_state();
|
||||
self.running_commands.clear();
|
||||
self.suppressed_exec_calls.clear();
|
||||
@@ -1900,7 +1903,8 @@ impl ChatWidget {
|
||||
match info {
|
||||
Some(info) => self.apply_token_info(info),
|
||||
None => {
|
||||
self.bottom_pane.set_context_window(None, None);
|
||||
self.bottom_pane
|
||||
.set_context_window(/*percent*/ None, /*used_tokens*/ None);
|
||||
self.token_info = None;
|
||||
}
|
||||
}
|
||||
@@ -1954,7 +1958,8 @@ impl ChatWidget {
|
||||
match saved {
|
||||
Some(info) => self.apply_token_info(info),
|
||||
None => {
|
||||
self.bottom_pane.set_context_window(None, None);
|
||||
self.bottom_pane
|
||||
.set_context_window(/*percent*/ None, /*used_tokens*/ None);
|
||||
self.token_info = None;
|
||||
}
|
||||
}
|
||||
@@ -2054,7 +2059,8 @@ impl ChatWidget {
|
||||
self.finalize_active_cell_as_failed();
|
||||
// Reset running state and clear streaming buffers.
|
||||
self.agent_turn_running = false;
|
||||
self.turn_sleep_inhibitor.set_turn_running(false);
|
||||
self.turn_sleep_inhibitor
|
||||
.set_turn_running(/*turn_running*/ false);
|
||||
self.update_task_running_state();
|
||||
self.running_commands.clear();
|
||||
self.suppressed_exec_calls.clear();
|
||||
@@ -2178,7 +2184,7 @@ impl ChatWidget {
|
||||
if send_pending_steers_immediately {
|
||||
self.add_to_history(history_cell::new_info_event(
|
||||
"Model interrupted to submit steer instructions.".to_owned(),
|
||||
None,
|
||||
/*hint*/ None,
|
||||
));
|
||||
} else {
|
||||
self.add_to_history(history_cell::new_error_event(
|
||||
@@ -2458,7 +2464,8 @@ impl ChatWidget {
|
||||
// review is pending. Parallel reviews are aggregated into one
|
||||
// footer summary by `PendingGuardianReviewStatus`.
|
||||
self.bottom_pane.ensure_status_indicator();
|
||||
self.bottom_pane.set_interrupt_hint_visible(true);
|
||||
self.bottom_pane
|
||||
.set_interrupt_hint_visible(/*visible*/ true);
|
||||
self.pending_guardian_review_status
|
||||
.start_or_update(ev.id.clone(), detail);
|
||||
if let Some(status) = self.pending_guardian_review_status.status_indicator_state() {
|
||||
@@ -2662,12 +2669,13 @@ impl ChatWidget {
|
||||
// Surface this in the status indicator (single "waiting" surface) instead of
|
||||
// the transcript. Keep the header short so the interrupt hint remains visible.
|
||||
self.bottom_pane.ensure_status_indicator();
|
||||
self.bottom_pane.set_interrupt_hint_visible(true);
|
||||
self.bottom_pane
|
||||
.set_interrupt_hint_visible(/*visible*/ true);
|
||||
self.set_status(
|
||||
"Waiting for background terminal".to_string(),
|
||||
command_display.clone(),
|
||||
StatusDetailsCapitalization::Preserve,
|
||||
1,
|
||||
/*details_max_lines*/ 1,
|
||||
);
|
||||
match &mut self.unified_exec_wait_streak {
|
||||
Some(wait) if wait.process_id == ev.process_id => {
|
||||
@@ -2917,7 +2925,8 @@ impl ChatWidget {
|
||||
fn on_background_event(&mut self, message: String) {
|
||||
debug!("BackgroundEvent: {message}");
|
||||
self.bottom_pane.ensure_status_indicator();
|
||||
self.bottom_pane.set_interrupt_hint_visible(true);
|
||||
self.bottom_pane
|
||||
.set_interrupt_hint_visible(/*visible*/ true);
|
||||
self.set_status_header(message);
|
||||
}
|
||||
|
||||
@@ -2930,7 +2939,7 @@ impl ChatWidget {
|
||||
message.push_str(": ");
|
||||
message.push_str(&status_message);
|
||||
}
|
||||
self.add_to_history(history_cell::new_info_event(message, None));
|
||||
self.add_to_history(history_cell::new_info_event(message, /*hint*/ None));
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
@@ -2954,7 +2963,8 @@ impl ChatWidget {
|
||||
|
||||
fn on_undo_started(&mut self, event: UndoStartedEvent) {
|
||||
self.bottom_pane.ensure_status_indicator();
|
||||
self.bottom_pane.set_interrupt_hint_visible(false);
|
||||
self.bottom_pane
|
||||
.set_interrupt_hint_visible(/*visible*/ false);
|
||||
let message = event
|
||||
.message
|
||||
.unwrap_or_else(|| "Undo in progress...".to_string());
|
||||
@@ -2972,7 +2982,7 @@ impl ChatWidget {
|
||||
}
|
||||
});
|
||||
if success {
|
||||
self.add_info_message(message, None);
|
||||
self.add_info_message(message, /*hint*/ None);
|
||||
} else {
|
||||
self.add_error_message(message);
|
||||
}
|
||||
@@ -3112,7 +3122,7 @@ impl ChatWidget {
|
||||
.map(|current| self.worked_elapsed_from(current));
|
||||
self.add_to_history(history_cell::FinalMessageSeparator::new(
|
||||
elapsed_seconds,
|
||||
None,
|
||||
/*runtime_metrics*/ None,
|
||||
));
|
||||
self.needs_final_message_separator = false;
|
||||
self.had_work_activity = false;
|
||||
@@ -3661,7 +3671,9 @@ impl ChatWidget {
|
||||
widget
|
||||
.bottom_pane
|
||||
.set_status_line_enabled(!widget.configured_status_line_items().is_empty());
|
||||
widget.bottom_pane.set_collaboration_modes_enabled(true);
|
||||
widget
|
||||
.bottom_pane
|
||||
.set_collaboration_modes_enabled(/*enabled*/ true);
|
||||
widget.sync_fast_command_enabled();
|
||||
widget.sync_personality_command_enabled();
|
||||
widget
|
||||
@@ -3847,7 +3859,9 @@ impl ChatWidget {
|
||||
widget
|
||||
.bottom_pane
|
||||
.set_status_line_enabled(!widget.configured_status_line_items().is_empty());
|
||||
widget.bottom_pane.set_collaboration_modes_enabled(true);
|
||||
widget
|
||||
.bottom_pane
|
||||
.set_collaboration_modes_enabled(/*enabled*/ true);
|
||||
widget.sync_fast_command_enabled();
|
||||
widget.sync_personality_command_enabled();
|
||||
widget
|
||||
@@ -4025,7 +4039,9 @@ impl ChatWidget {
|
||||
widget
|
||||
.bottom_pane
|
||||
.set_status_line_enabled(!widget.configured_status_line_items().is_empty());
|
||||
widget.bottom_pane.set_collaboration_modes_enabled(true);
|
||||
widget
|
||||
.bottom_pane
|
||||
.set_collaboration_modes_enabled(/*enabled*/ true);
|
||||
widget.sync_fast_command_enabled();
|
||||
widget.sync_personality_command_enabled();
|
||||
widget
|
||||
@@ -4326,7 +4342,7 @@ impl ChatWidget {
|
||||
let message = format!(
|
||||
"{DEFAULT_PROJECT_DOC_FILENAME} already exists here. Skipping /init to avoid overwriting it."
|
||||
);
|
||||
self.add_info_message(message, None);
|
||||
self.add_info_message(message, /*hint*/ None);
|
||||
return;
|
||||
}
|
||||
const INIT_PROMPT: &str = include_str!("../prompt_for_init_command.md");
|
||||
@@ -4341,7 +4357,7 @@ impl ChatWidget {
|
||||
}
|
||||
SlashCommand::Rename => {
|
||||
self.session_telemetry
|
||||
.counter("codex.thread.rename", 1, &[]);
|
||||
.counter("codex.thread.rename", /*inc*/ 1, &[]);
|
||||
self.show_rename_prompt();
|
||||
}
|
||||
SlashCommand::Model => {
|
||||
@@ -4360,7 +4376,7 @@ impl ChatWidget {
|
||||
return;
|
||||
}
|
||||
if self.realtime_conversation.is_live() {
|
||||
self.request_realtime_conversation_close(None);
|
||||
self.request_realtime_conversation_close(/*info_message*/ None);
|
||||
} else {
|
||||
self.start_realtime_conversation();
|
||||
}
|
||||
@@ -4385,7 +4401,10 @@ impl ChatWidget {
|
||||
if let Some(mask) = collaboration_modes::plan_mask(self.models_manager.as_ref()) {
|
||||
self.set_collaboration_mask(mask);
|
||||
} else {
|
||||
self.add_info_message("Plan mode unavailable right now.".to_string(), None);
|
||||
self.add_info_message(
|
||||
"Plan mode unavailable right now.".to_string(),
|
||||
/*hint*/ None,
|
||||
);
|
||||
}
|
||||
}
|
||||
SlashCommand::Collab => {
|
||||
@@ -4502,7 +4521,7 @@ impl ChatWidget {
|
||||
self.add_info_message(
|
||||
"`/copy` is unavailable before the first Codex output or right after a rollback."
|
||||
.to_string(),
|
||||
None,
|
||||
/*hint*/ None,
|
||||
);
|
||||
return;
|
||||
};
|
||||
@@ -4565,10 +4584,13 @@ impl ChatWidget {
|
||||
if let Some(path) = self.rollout_path() {
|
||||
self.add_info_message(
|
||||
format!("Current rollout path: {}", path.display()),
|
||||
None,
|
||||
/*hint*/ None,
|
||||
);
|
||||
} else {
|
||||
self.add_info_message("Rollout path is not available yet.".to_string(), None);
|
||||
self.add_info_message(
|
||||
"Rollout path is not available yet.".to_string(),
|
||||
/*hint*/ None,
|
||||
);
|
||||
}
|
||||
}
|
||||
SlashCommand::TestApproval => {
|
||||
@@ -4641,7 +4663,7 @@ impl ChatWidget {
|
||||
}
|
||||
match trimmed.to_ascii_lowercase().as_str() {
|
||||
"on" => self.set_service_tier_selection(Some(ServiceTier::Fast)),
|
||||
"off" => self.set_service_tier_selection(None),
|
||||
"off" => self.set_service_tier_selection(/*service_tier*/ None),
|
||||
"status" => {
|
||||
let status = if matches!(self.config.service_tier, Some(ServiceTier::Fast))
|
||||
{
|
||||
@@ -4649,7 +4671,10 @@ impl ChatWidget {
|
||||
} else {
|
||||
"off"
|
||||
};
|
||||
self.add_info_message(format!("Fast mode is {status}."), None);
|
||||
self.add_info_message(
|
||||
format!("Fast mode is {status}."),
|
||||
/*hint*/ None,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
self.add_error_message("Usage: /fast [on|off|status]".to_string());
|
||||
@@ -4658,9 +4683,10 @@ impl ChatWidget {
|
||||
}
|
||||
SlashCommand::Rename if !trimmed.is_empty() => {
|
||||
self.session_telemetry
|
||||
.counter("codex.thread.rename", 1, &[]);
|
||||
let Some((prepared_args, _prepared_elements)) =
|
||||
self.bottom_pane.prepare_inline_args_submission(false)
|
||||
.counter("codex.thread.rename", /*inc*/ 1, &[]);
|
||||
let Some((prepared_args, _prepared_elements)) = self
|
||||
.bottom_pane
|
||||
.prepare_inline_args_submission(/*record_history*/ false)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
@@ -4680,8 +4706,9 @@ impl ChatWidget {
|
||||
if self.active_mode_kind() != ModeKind::Plan {
|
||||
return;
|
||||
}
|
||||
let Some((prepared_args, prepared_elements)) =
|
||||
self.bottom_pane.prepare_inline_args_submission(true)
|
||||
let Some((prepared_args, prepared_elements)) = self
|
||||
.bottom_pane
|
||||
.prepare_inline_args_submission(/*record_history*/ true)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
@@ -4706,8 +4733,9 @@ impl ChatWidget {
|
||||
}
|
||||
}
|
||||
SlashCommand::Review if !trimmed.is_empty() => {
|
||||
let Some((prepared_args, _prepared_elements)) =
|
||||
self.bottom_pane.prepare_inline_args_submission(false)
|
||||
let Some((prepared_args, _prepared_elements)) = self
|
||||
.bottom_pane
|
||||
.prepare_inline_args_submission(/*record_history*/ false)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
@@ -4722,8 +4750,9 @@ impl ChatWidget {
|
||||
self.bottom_pane.drain_pending_submission_state();
|
||||
}
|
||||
SlashCommand::SandboxReadRoot if !trimmed.is_empty() => {
|
||||
let Some((prepared_args, _prepared_elements)) =
|
||||
self.bottom_pane.prepare_inline_args_submission(false)
|
||||
let Some((prepared_args, _prepared_elements)) = self
|
||||
.bottom_pane
|
||||
.prepare_inline_args_submission(/*record_history*/ false)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
@@ -4752,7 +4781,7 @@ impl ChatWidget {
|
||||
let view = CustomPromptView::new(
|
||||
title.to_string(),
|
||||
"Type a name and press Enter".to_string(),
|
||||
None,
|
||||
/*context_label*/ None,
|
||||
Box::new(move |name: String| {
|
||||
let Some(name) = codex_core::util::normalize_thread_name(&name) else {
|
||||
tx.send(AppEvent::InsertHistoryCell(Box::new(
|
||||
@@ -5160,13 +5189,17 @@ impl ChatWidget {
|
||||
continue;
|
||||
}
|
||||
// `id: None` indicates a synthetic/fake id coming from replay.
|
||||
self.dispatch_event_msg(None, msg, Some(ReplayKind::ResumeInitialMessages));
|
||||
self.dispatch_event_msg(
|
||||
/*id*/ None,
|
||||
msg,
|
||||
Some(ReplayKind::ResumeInitialMessages),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_codex_event(&mut self, event: Event) {
|
||||
let Event { id, msg } = event;
|
||||
self.dispatch_event_msg(Some(id), msg, None);
|
||||
self.dispatch_event_msg(Some(id), msg, /*replay_kind*/ None);
|
||||
}
|
||||
|
||||
pub(crate) fn handle_codex_event_replay(&mut self, event: Event) {
|
||||
@@ -5174,7 +5207,7 @@ impl ChatWidget {
|
||||
if matches!(msg, EventMsg::ShutdownComplete) {
|
||||
return;
|
||||
}
|
||||
self.dispatch_event_msg(None, msg, Some(ReplayKind::ThreadSnapshot));
|
||||
self.dispatch_event_msg(/*id*/ None, msg, Some(ReplayKind::ThreadSnapshot));
|
||||
}
|
||||
|
||||
/// Dispatch a protocol `EventMsg` to the appropriate handler.
|
||||
@@ -5479,7 +5512,7 @@ impl ChatWidget {
|
||||
}
|
||||
// Avoid toggling running state for replayed history events on resume.
|
||||
if !from_replay && !self.bottom_pane.is_task_running() {
|
||||
self.bottom_pane.set_task_running(true);
|
||||
self.bottom_pane.set_task_running(/*running*/ true);
|
||||
}
|
||||
self.is_review_mode = true;
|
||||
let hint = review
|
||||
@@ -5509,11 +5542,11 @@ impl ChatWidget {
|
||||
let mut rendered: Vec<ratatui::text::Line<'static>> = vec!["".into()];
|
||||
append_markdown(
|
||||
&explanation,
|
||||
None,
|
||||
/*width*/ None,
|
||||
Some(self.config.cwd.as_path()),
|
||||
&mut rendered,
|
||||
);
|
||||
let body_cell = AgentMessageCell::new(rendered, false);
|
||||
let body_cell = AgentMessageCell::new(rendered, /*is_first_line*/ false);
|
||||
self.app_event_tx
|
||||
.send(AppEvent::InsertHistoryCell(Box::new(body_cell)));
|
||||
}
|
||||
@@ -5757,7 +5790,10 @@ impl ChatWidget {
|
||||
|
||||
self.config
|
||||
.config_layer_stack
|
||||
.get_layers(ConfigLayerStackOrdering::LowestPrecedenceFirst, true)
|
||||
.get_layers(
|
||||
ConfigLayerStackOrdering::LowestPrecedenceFirst,
|
||||
/*include_disabled*/ true,
|
||||
)
|
||||
.iter()
|
||||
.find_map(|layer| match &layer.name {
|
||||
ConfigLayerSource::Project { dot_codex_folder } => {
|
||||
@@ -5771,7 +5807,7 @@ impl ChatWidget {
|
||||
self.status_line_project_root().map(|root| {
|
||||
root.file_name()
|
||||
.map(|name| name.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|| format_directory_display(&root, None))
|
||||
.unwrap_or_else(|| format_directory_display(&root, /*max_width*/ None))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5830,7 +5866,10 @@ impl ChatWidget {
|
||||
Some(format!("{} {label}{fast_label}", self.model_display_name()))
|
||||
}
|
||||
StatusLineItem::CurrentDir => {
|
||||
Some(format_directory_display(self.status_line_cwd(), None))
|
||||
Some(format_directory_display(
|
||||
self.status_line_cwd(),
|
||||
/*max_width*/ None,
|
||||
))
|
||||
}
|
||||
StatusLineItem::ProjectRoot => self.status_line_project_root_name(),
|
||||
StatusLineItem::GitBranch => self.status_line_branch.clone(),
|
||||
@@ -5965,7 +6004,10 @@ impl ChatWidget {
|
||||
|
||||
fn clean_background_terminals(&mut self) {
|
||||
self.submit_op(Op::CleanBackgroundTerminals);
|
||||
self.add_info_message("Stopping all background terminals.".to_string(), None);
|
||||
self.add_info_message(
|
||||
"Stopping all background terminals.".to_string(),
|
||||
/*hint*/ None,
|
||||
);
|
||||
}
|
||||
|
||||
fn stop_rate_limit_poller(&mut self) {
|
||||
@@ -5979,7 +6021,7 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
fn prefetch_connectors(&mut self) {
|
||||
self.prefetch_connectors_with_options(false);
|
||||
self.prefetch_connectors_with_options(/*force_refetch*/ false);
|
||||
}
|
||||
|
||||
fn prefetch_connectors_with_options(&mut self, force_refetch: bool) {
|
||||
@@ -6034,7 +6076,7 @@ impl ChatWidget {
|
||||
let connectors = connectors::merge_connectors_with_accessible(
|
||||
all_connectors,
|
||||
accessible_connectors,
|
||||
true,
|
||||
/*all_connectors_loaded*/ true,
|
||||
);
|
||||
Ok(ConnectorsSnapshot { connectors })
|
||||
}
|
||||
@@ -6209,7 +6251,7 @@ impl ChatWidget {
|
||||
if !self.is_session_configured() {
|
||||
self.add_info_message(
|
||||
"Model selection is disabled until startup completes.".to_string(),
|
||||
None,
|
||||
/*hint*/ None,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -6219,7 +6261,7 @@ impl ChatWidget {
|
||||
Err(_) => {
|
||||
self.add_info_message(
|
||||
"Models are being updated; please try /model again in a moment.".to_string(),
|
||||
None,
|
||||
/*hint*/ None,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -6231,7 +6273,7 @@ impl ChatWidget {
|
||||
if !self.is_session_configured() {
|
||||
self.add_info_message(
|
||||
"Personality selection is disabled until startup completes.".to_string(),
|
||||
None,
|
||||
/*hint*/ None,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -6592,7 +6634,7 @@ impl ChatWidget {
|
||||
if presets.is_empty() {
|
||||
self.add_info_message(
|
||||
"No additional models are available right now.".to_string(),
|
||||
None,
|
||||
/*hint*/ None,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -6638,7 +6680,7 @@ impl ChatWidget {
|
||||
if presets.is_empty() {
|
||||
self.add_info_message(
|
||||
"No collaboration modes are available right now.".to_string(),
|
||||
None,
|
||||
/*hint*/ None,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -7255,7 +7297,10 @@ impl ChatWidget {
|
||||
tx.send(AppEvent::UpdateSandboxPolicy(sandbox_clone));
|
||||
tx.send(AppEvent::UpdateApprovalsReviewer(approvals_reviewer));
|
||||
tx.send(AppEvent::InsertHistoryCell(Box::new(
|
||||
history_cell::new_info_event(format!("Permissions updated to {label}"), None),
|
||||
history_cell::new_info_event(
|
||||
format!("Permissions updated to {label}"),
|
||||
/*hint*/ None,
|
||||
),
|
||||
)));
|
||||
})]
|
||||
}
|
||||
@@ -7899,9 +7944,11 @@ impl ChatWidget {
|
||||
|
||||
/// Set the reasoning effort in the stored collaboration mode.
|
||||
pub(crate) fn set_reasoning_effort(&mut self, effort: Option<ReasoningEffortConfig>) {
|
||||
self.current_collaboration_mode =
|
||||
self.current_collaboration_mode
|
||||
.with_updates(None, Some(effort), None);
|
||||
self.current_collaboration_mode = self.current_collaboration_mode.with_updates(
|
||||
/*model*/ None,
|
||||
Some(effort),
|
||||
/*developer_instructions*/ None,
|
||||
);
|
||||
if self.collaboration_modes_enabled()
|
||||
&& let Some(mask) = self.active_collaboration_mask.as_mut()
|
||||
&& mask.mode != Some(ModeKind::Plan)
|
||||
@@ -7962,9 +8009,11 @@ impl ChatWidget {
|
||||
|
||||
/// Set the model in the widget's config copy and stored collaboration mode.
|
||||
pub(crate) fn set_model(&mut self, model: &str) {
|
||||
self.current_collaboration_mode =
|
||||
self.current_collaboration_mode
|
||||
.with_updates(Some(model.to_string()), None, None);
|
||||
self.current_collaboration_mode = self.current_collaboration_mode.with_updates(
|
||||
Some(model.to_string()),
|
||||
/*effort*/ None,
|
||||
/*developer_instructions*/ None,
|
||||
);
|
||||
if self.collaboration_modes_enabled()
|
||||
&& let Some(mask) = self.active_collaboration_mask.as_mut()
|
||||
{
|
||||
@@ -8248,7 +8297,7 @@ impl ChatWidget {
|
||||
message.push_str(" for ");
|
||||
message.push_str(next_mode.display_name());
|
||||
message.push_str(" mode.");
|
||||
self.add_info_message(message, None);
|
||||
self.add_info_message(message, /*hint*/ None);
|
||||
}
|
||||
self.request_redraw();
|
||||
}
|
||||
@@ -8288,8 +8337,8 @@ impl ChatWidget {
|
||||
Box::new(history_cell::SessionHeaderHistoryCell::new_with_style(
|
||||
DEFAULT_MODEL_DISPLAY_NAME.to_string(),
|
||||
placeholder_style,
|
||||
None,
|
||||
false,
|
||||
/*reasoning_effort*/ None,
|
||||
/*show_fast_status*/ false,
|
||||
config.cwd.clone(),
|
||||
CODEX_CLI_VERSION,
|
||||
))
|
||||
@@ -8356,7 +8405,10 @@ impl ChatWidget {
|
||||
let mcp_manager = McpManager::new(Arc::new(PluginsManager::new(
|
||||
self.config.codex_home.clone(),
|
||||
)));
|
||||
if mcp_manager.effective_servers(&self.config, None).is_empty() {
|
||||
if mcp_manager
|
||||
.effective_servers(&self.config, /*auth*/ None)
|
||||
.is_empty()
|
||||
{
|
||||
self.add_to_history(history_cell::empty_mcp_output());
|
||||
} else {
|
||||
self.submit_op(Op::ListMcpTools);
|
||||
@@ -8380,7 +8432,7 @@ impl ChatWidget {
|
||||
match connectors_cache {
|
||||
ConnectorsCacheState::Ready(snapshot) => {
|
||||
if snapshot.connectors.is_empty() {
|
||||
self.add_info_message("No apps available.".to_string(), None);
|
||||
self.add_info_message("No apps available.".to_string(), /*hint*/ None);
|
||||
} else {
|
||||
self.open_connectors_popup(&snapshot.connectors);
|
||||
}
|
||||
@@ -8406,8 +8458,9 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
fn open_connectors_popup(&mut self, connectors: &[connectors::AppInfo]) {
|
||||
self.bottom_pane
|
||||
.show_selection_view(self.connectors_popup_params(connectors, None));
|
||||
self.bottom_pane.show_selection_view(
|
||||
self.connectors_popup_params(connectors, /*selected_connector_id*/ None),
|
||||
);
|
||||
}
|
||||
|
||||
fn connectors_loading_popup_params(&self) -> SelectionViewParams {
|
||||
@@ -8502,7 +8555,10 @@ impl ChatWidget {
|
||||
let missing_label_for_action = missing_label.clone();
|
||||
item.actions = vec![Box::new(move |tx| {
|
||||
tx.send(AppEvent::InsertHistoryCell(Box::new(
|
||||
history_cell::new_info_event(missing_label_for_action.clone(), None),
|
||||
history_cell::new_info_event(
|
||||
missing_label_for_action.clone(),
|
||||
/*hint*/ None,
|
||||
),
|
||||
)));
|
||||
})];
|
||||
item.dismiss_on_select = true;
|
||||
@@ -8797,7 +8853,7 @@ impl ChatWidget {
|
||||
// Record outbound operation for session replay fidelity.
|
||||
crate::session_log::log_outbound_op(&op);
|
||||
if matches!(&op, Op::Review { .. }) && !self.bottom_pane.is_task_running() {
|
||||
self.bottom_pane.set_task_running(true);
|
||||
self.bottom_pane.set_task_running(/*running*/ true);
|
||||
}
|
||||
if let Err(e) = self.codex_op_tx.send(op) {
|
||||
tracing::error!("failed to submit op: {e}");
|
||||
@@ -8848,7 +8904,7 @@ impl ChatWidget {
|
||||
snapshot.connectors = connectors::merge_connectors_with_accessible(
|
||||
Vec::new(),
|
||||
snapshot.connectors,
|
||||
false,
|
||||
/*all_connectors_loaded*/ false,
|
||||
);
|
||||
}
|
||||
snapshot.connectors =
|
||||
@@ -8889,13 +8945,13 @@ impl ChatWidget {
|
||||
self.bottom_pane.set_connectors_snapshot(Some(snapshot));
|
||||
} else {
|
||||
self.connectors_cache = ConnectorsCacheState::Failed(err);
|
||||
self.bottom_pane.set_connectors_snapshot(None);
|
||||
self.bottom_pane.set_connectors_snapshot(/*snapshot*/ None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if trigger_pending_force_refetch {
|
||||
self.prefetch_connectors_with_options(true);
|
||||
self.prefetch_connectors_with_options(/*force_refetch*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8924,7 +8980,7 @@ impl ChatWidget {
|
||||
|
||||
fn refresh_plugin_mentions(&mut self) {
|
||||
if !self.config.features.enabled(Feature::Plugins) {
|
||||
self.bottom_pane.set_plugin_mentions(None);
|
||||
self.bottom_pane.set_plugin_mentions(/*plugins*/ None);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -9033,7 +9089,7 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
pub(crate) async fn show_review_commit_picker(&mut self, cwd: &Path) {
|
||||
let commits = codex_core::git_info::recent_commits(cwd, 100).await;
|
||||
let commits = codex_core::git_info::recent_commits(cwd, /*limit*/ 100).await;
|
||||
|
||||
let mut items: Vec<SelectionItem> = Vec::with_capacity(commits.len());
|
||||
for entry in commits {
|
||||
@@ -9075,7 +9131,7 @@ impl ChatWidget {
|
||||
let view = CustomPromptView::new(
|
||||
"Custom review instructions".to_string(),
|
||||
"Type instructions and press Enter".to_string(),
|
||||
None,
|
||||
/*context_label*/ None,
|
||||
Box::new(move |prompt: String| {
|
||||
let trimmed = prompt.trim().to_string();
|
||||
if trimmed.is_empty() {
|
||||
@@ -9166,14 +9222,18 @@ impl ChatWidget {
|
||||
|
||||
fn as_renderable(&self) -> RenderableItem<'_> {
|
||||
let active_cell_renderable = match &self.active_cell {
|
||||
Some(cell) => RenderableItem::Borrowed(cell).inset(Insets::tlbr(1, 0, 0, 0)),
|
||||
Some(cell) => RenderableItem::Borrowed(cell).inset(Insets::tlbr(
|
||||
/*top*/ 1, /*left*/ 0, /*bottom*/ 0, /*right*/ 0,
|
||||
)),
|
||||
None => RenderableItem::Owned(Box::new(())),
|
||||
};
|
||||
let mut flex = FlexRenderable::new();
|
||||
flex.push(1, active_cell_renderable);
|
||||
flex.push(/*flex*/ 1, active_cell_renderable);
|
||||
flex.push(
|
||||
0,
|
||||
RenderableItem::Borrowed(&self.bottom_pane).inset(Insets::tlbr(1, 0, 0, 0)),
|
||||
/*flex*/ 0,
|
||||
RenderableItem::Borrowed(&self.bottom_pane).inset(Insets::tlbr(
|
||||
/*top*/ 1, /*left*/ 0, /*bottom*/ 0, /*right*/ 0,
|
||||
)),
|
||||
);
|
||||
RenderableItem::Owned(Box::new(flex))
|
||||
}
|
||||
@@ -9265,7 +9325,10 @@ impl Notification {
|
||||
.unwrap_or_else(|| "Agent turn complete".to_string())
|
||||
}
|
||||
Notification::ExecApprovalRequested { command } => {
|
||||
format!("Approval requested: {}", truncate_text(command, 30))
|
||||
format!(
|
||||
"Approval requested: {}",
|
||||
truncate_text(command, /*max_graphemes*/ 30)
|
||||
)
|
||||
}
|
||||
Notification::EditApprovalRequested { cwd, changes } => {
|
||||
format!(
|
||||
@@ -9352,7 +9415,7 @@ impl Notification {
|
||||
if summary.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(truncate_text(summary, 30))
|
||||
Some(truncate_text(summary, /*max_graphemes*/ 30))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ pub(crate) fn spawn_agent(
|
||||
tracing::error!("{message}");
|
||||
app_event_tx_clone.send(AppEvent::CodexEvent(Event {
|
||||
id: "".to_string(),
|
||||
msg: EventMsg::Error(err.to_error_event(None)),
|
||||
msg: EventMsg::Error(err.to_error_event(/*message_prefix*/ None)),
|
||||
}));
|
||||
app_event_tx_clone.send(AppEvent::FatalExitRequest(message));
|
||||
tracing::error!("failed to initialize codex: {err}");
|
||||
|
||||
@@ -183,7 +183,7 @@ impl ChatWidget {
|
||||
self.realtime_conversation.warned_audio_only_submission = true;
|
||||
self.add_info_message(
|
||||
"Realtime voice mode is audio-only. Use /realtime to stop.".to_string(),
|
||||
None,
|
||||
/*hint*/ None,
|
||||
);
|
||||
} else {
|
||||
self.request_redraw();
|
||||
@@ -212,7 +212,7 @@ impl ChatWidget {
|
||||
pub(super) fn request_realtime_conversation_close(&mut self, info_message: Option<String>) {
|
||||
if !self.realtime_conversation.is_live() {
|
||||
if let Some(message) = info_message {
|
||||
self.add_info_message(message, None);
|
||||
self.add_info_message(message, /*hint*/ None);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -221,10 +221,10 @@ impl ChatWidget {
|
||||
self.realtime_conversation.phase = RealtimeConversationPhase::Stopping;
|
||||
self.submit_op(Op::RealtimeConversationClose);
|
||||
self.stop_realtime_local_audio();
|
||||
self.set_footer_hint_override(None);
|
||||
self.set_footer_hint_override(/*items*/ None);
|
||||
|
||||
if let Some(message) = info_message {
|
||||
self.add_info_message(message, None);
|
||||
self.add_info_message(message, /*hint*/ None);
|
||||
} else {
|
||||
self.request_redraw();
|
||||
}
|
||||
@@ -232,7 +232,7 @@ impl ChatWidget {
|
||||
|
||||
pub(super) fn reset_realtime_conversation_state(&mut self) {
|
||||
self.stop_realtime_local_audio();
|
||||
self.set_footer_hint_override(None);
|
||||
self.set_footer_hint_override(/*items*/ None);
|
||||
self.realtime_conversation.phase = RealtimeConversationPhase::Inactive;
|
||||
self.realtime_conversation.requested_close = false;
|
||||
self.realtime_conversation.session_id = None;
|
||||
@@ -282,7 +282,10 @@ impl ChatWidget {
|
||||
let reason = ev.reason;
|
||||
self.reset_realtime_conversation_state();
|
||||
if !requested && let Some(reason) = reason {
|
||||
self.add_info_message(format!("Realtime voice mode closed: {reason}"), None);
|
||||
self.add_info_message(
|
||||
format!("Realtime voice mode closed: {reason}"),
|
||||
/*hint*/ None,
|
||||
);
|
||||
}
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ impl ChatWidget {
|
||||
|
||||
pub(crate) fn open_manage_skills_popup(&mut self) {
|
||||
if self.skills_all.is_empty() {
|
||||
self.add_info_message("No skills available.".to_string(), None);
|
||||
self.add_info_message("No skills available.".to_string(), /*hint*/ None);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ impl ChatWidget {
|
||||
}
|
||||
self.add_info_message(
|
||||
format!("{enabled_count} skills enabled, {disabled_count} skills disabled"),
|
||||
None,
|
||||
/*hint*/ None,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -212,20 +212,23 @@ impl WidgetRef for &CwdPromptScreen {
|
||||
"Session = latest cwd recorded in the {action_past} session"
|
||||
))
|
||||
.dim()
|
||||
.inset(Insets::tlbr(0, 2, 0, 0)),
|
||||
.inset(Insets::tlbr(
|
||||
/*top*/ 0, /*left*/ 2, /*bottom*/ 0, /*right*/ 0,
|
||||
)),
|
||||
);
|
||||
column.push(
|
||||
Line::from("Current = your current working directory".dim())
|
||||
.inset(Insets::tlbr(0, 2, 0, 0)),
|
||||
Line::from("Current = your current working directory".dim()).inset(Insets::tlbr(
|
||||
/*top*/ 0, /*left*/ 2, /*bottom*/ 0, /*right*/ 0,
|
||||
)),
|
||||
);
|
||||
column.push("");
|
||||
column.push(selection_option_row(
|
||||
0,
|
||||
/*index*/ 0,
|
||||
format!("Use session directory ({session_cwd})"),
|
||||
self.highlighted == CwdSelection::Session,
|
||||
));
|
||||
column.push(selection_option_row(
|
||||
1,
|
||||
/*index*/ 1,
|
||||
format!("Use current directory ({current_cwd})"),
|
||||
self.highlighted == CwdSelection::Current,
|
||||
));
|
||||
@@ -236,7 +239,9 @@ impl WidgetRef for &CwdPromptScreen {
|
||||
key_hint::plain(KeyCode::Enter).into(),
|
||||
" to continue".dim(),
|
||||
])
|
||||
.inset(Insets::tlbr(0, 2, 0, 0)),
|
||||
.inset(Insets::tlbr(
|
||||
/*top*/ 0, /*left*/ 2, /*bottom*/ 0, /*right*/ 0,
|
||||
)),
|
||||
);
|
||||
column.render(area, buf);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,10 @@ fn render_debug_config_lines(stack: &ConfigLayerStack) -> Vec<Line<'static>> {
|
||||
.bold()
|
||||
.into(),
|
||||
);
|
||||
let layers = stack.get_layers(ConfigLayerStackOrdering::LowestPrecedenceFirst, true);
|
||||
let layers = stack.get_layers(
|
||||
ConfigLayerStackOrdering::LowestPrecedenceFirst,
|
||||
/*include_disabled*/ true,
|
||||
);
|
||||
if layers.is_empty() {
|
||||
lines.push(" <none>".dim().into());
|
||||
} else {
|
||||
@@ -186,7 +189,7 @@ fn render_non_file_layer_details(layer: &ConfigLayerEntry) -> Vec<Line<'static>>
|
||||
|
||||
fn render_session_flag_details(config: &TomlValue) -> Vec<Line<'static>> {
|
||||
let mut pairs = Vec::new();
|
||||
flatten_toml_key_values(config, None, &mut pairs);
|
||||
flatten_toml_key_values(config, /*prefix*/ None, &mut pairs);
|
||||
|
||||
if pairs.is_empty() {
|
||||
return vec![" - <none>".dim().into()];
|
||||
|
||||
@@ -306,13 +306,13 @@ impl DiffSummary {
|
||||
impl Renderable for FileChange {
|
||||
fn render(&self, area: Rect, buf: &mut Buffer) {
|
||||
let mut lines = vec![];
|
||||
render_change(self, &mut lines, area.width as usize, None);
|
||||
render_change(self, &mut lines, area.width as usize, /*lang*/ None);
|
||||
Paragraph::new(lines).render(area, buf);
|
||||
}
|
||||
|
||||
fn desired_height(&self, width: u16) -> u16 {
|
||||
let mut lines = vec![];
|
||||
render_change(self, &mut lines, width as usize, None);
|
||||
render_change(self, &mut lines, width as usize, /*lang*/ None);
|
||||
lines.len() as u16
|
||||
}
|
||||
}
|
||||
@@ -332,7 +332,9 @@ impl From<DiffSummary> for Box<dyn Renderable> {
|
||||
rows.push(Box::new(RtLine::from("")));
|
||||
rows.push(Box::new(InsetRenderable::new(
|
||||
Box::new(row.change) as Box<dyn Renderable>,
|
||||
Insets::tlbr(0, 2, 0, 0),
|
||||
Insets::tlbr(
|
||||
/*top*/ 0, /*left*/ 2, /*bottom*/ 0, /*right*/ 0,
|
||||
),
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -502,7 +504,7 @@ fn render_change(
|
||||
raw,
|
||||
width,
|
||||
line_number_width,
|
||||
None,
|
||||
/*syntax_spans*/ None,
|
||||
style_context.theme,
|
||||
style_context.color_level,
|
||||
style_context.diff_backgrounds,
|
||||
@@ -534,7 +536,7 @@ fn render_change(
|
||||
raw,
|
||||
width,
|
||||
line_number_width,
|
||||
None,
|
||||
/*syntax_spans*/ None,
|
||||
style_context.theme,
|
||||
style_context.color_level,
|
||||
style_context.diff_backgrounds,
|
||||
@@ -649,7 +651,7 @@ fn render_change(
|
||||
s,
|
||||
width,
|
||||
line_number_width,
|
||||
None,
|
||||
/*syntax_spans*/ None,
|
||||
style_context.theme,
|
||||
style_context.color_level,
|
||||
style_context.diff_backgrounds,
|
||||
@@ -682,7 +684,7 @@ fn render_change(
|
||||
s,
|
||||
width,
|
||||
line_number_width,
|
||||
None,
|
||||
/*syntax_spans*/ None,
|
||||
style_context.theme,
|
||||
style_context.color_level,
|
||||
style_context.diff_backgrounds,
|
||||
@@ -715,7 +717,7 @@ fn render_change(
|
||||
s,
|
||||
width,
|
||||
line_number_width,
|
||||
None,
|
||||
/*syntax_spans*/ None,
|
||||
style_context.theme,
|
||||
style_context.color_level,
|
||||
style_context.diff_backgrounds,
|
||||
@@ -796,7 +798,7 @@ pub(crate) fn push_wrapped_diff_line_with_style_context(
|
||||
text,
|
||||
width,
|
||||
line_number_width,
|
||||
None,
|
||||
/*syntax_spans*/ None,
|
||||
style_context.theme,
|
||||
style_context.color_level,
|
||||
style_context.diff_backgrounds,
|
||||
|
||||
@@ -681,9 +681,9 @@ impl ExecDisplayLayout {
|
||||
|
||||
const EXEC_DISPLAY_LAYOUT: ExecDisplayLayout = ExecDisplayLayout::new(
|
||||
PrefixedBlock::new(" │ ", " │ "),
|
||||
2,
|
||||
/*command_continuation_max_lines*/ 2,
|
||||
PrefixedBlock::new(" └ ", " "),
|
||||
5,
|
||||
/*output_max_lines*/ 5,
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -87,7 +87,7 @@ impl FileSearchManager {
|
||||
..Default::default()
|
||||
},
|
||||
reporter,
|
||||
None,
|
||||
/*cancel_flag*/ None,
|
||||
);
|
||||
match session {
|
||||
Ok(session) => st.session = Some(session),
|
||||
|
||||
@@ -782,7 +782,7 @@ fn truncate_exec_snippet(full_cmd: &str) -> String {
|
||||
Some((first, _)) => format!("{first} ..."),
|
||||
None => full_cmd.to_string(),
|
||||
};
|
||||
snippet = truncate_text(&snippet, 80);
|
||||
snippet = truncate_text(&snippet, /*max_graphemes*/ 80);
|
||||
snippet
|
||||
}
|
||||
|
||||
@@ -1003,7 +1003,7 @@ pub(crate) fn card_inner_width(width: u16, max_inner_width: usize) -> Option<usi
|
||||
|
||||
/// Render `lines` inside a border sized to the widest span in the content.
|
||||
pub(crate) fn with_border(lines: Vec<Line<'static>>) -> Vec<Line<'static>> {
|
||||
with_border_internal(lines, None)
|
||||
with_border_internal(lines, /*forced_inner_width*/ None)
|
||||
}
|
||||
|
||||
/// Render `lines` inside a border whose inner width is at least `inner_width`.
|
||||
@@ -1660,7 +1660,7 @@ pub(crate) fn new_active_web_search_call(
|
||||
query: String,
|
||||
animations_enabled: bool,
|
||||
) -> WebSearchCell {
|
||||
WebSearchCell::new(call_id, query, None, animations_enabled)
|
||||
WebSearchCell::new(call_id, query, /*action*/ None, animations_enabled)
|
||||
}
|
||||
|
||||
pub(crate) fn new_web_search_call(
|
||||
@@ -1668,7 +1668,12 @@ pub(crate) fn new_web_search_call(
|
||||
query: String,
|
||||
action: WebSearchAction,
|
||||
) -> WebSearchCell {
|
||||
let mut cell = WebSearchCell::new(call_id, query, Some(action), false);
|
||||
let mut cell = WebSearchCell::new(
|
||||
call_id,
|
||||
query,
|
||||
Some(action),
|
||||
/*animations_enabled*/ false,
|
||||
);
|
||||
cell.complete();
|
||||
cell
|
||||
}
|
||||
@@ -1812,7 +1817,7 @@ pub(crate) fn new_mcp_tools_output(
|
||||
}
|
||||
|
||||
let mcp_manager = McpManager::new(Arc::new(PluginsManager::new(config.codex_home.clone())));
|
||||
let effective_servers = mcp_manager.effective_servers(config, None);
|
||||
let effective_servers = mcp_manager.effective_servers(config, /*auth*/ None);
|
||||
let mut servers: Vec<_> = effective_servers.iter().collect();
|
||||
servers.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||
|
||||
@@ -2345,7 +2350,7 @@ pub(crate) fn new_reasoning_summary_block(
|
||||
header_buffer,
|
||||
summary_buffer,
|
||||
&cwd,
|
||||
false,
|
||||
/*transcript_only*/ false,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -2354,7 +2359,7 @@ pub(crate) fn new_reasoning_summary_block(
|
||||
"".to_string(),
|
||||
full_reasoning_buffer.to_string(),
|
||||
&cwd,
|
||||
true,
|
||||
/*transcript_only*/ true,
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
+53
-34
@@ -397,7 +397,7 @@ pub async fn run_main(
|
||||
|
||||
let cloud_auth_manager = AuthManager::shared(
|
||||
codex_home.to_path_buf(),
|
||||
false,
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
config_toml.cli_auth_credentials_store.unwrap_or_default(),
|
||||
);
|
||||
let chatgpt_base_url = config_toml
|
||||
@@ -560,7 +560,12 @@ pub async fn run_main(
|
||||
}
|
||||
|
||||
let otel = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||
codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"), None, true)
|
||||
codex_core::otel_init::build_provider(
|
||||
&config,
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
/*service_name_override*/ None,
|
||||
/*default_analytics_enabled*/ true,
|
||||
)
|
||||
})) {
|
||||
Ok(Ok(otel)) => otel,
|
||||
Ok(Err(e)) => {
|
||||
@@ -666,7 +671,7 @@ async fn run_ratatui_app(
|
||||
|
||||
let auth_manager = AuthManager::shared(
|
||||
initial_config.codex_home.clone(),
|
||||
false,
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
initial_config.cli_auth_credentials_store_mode,
|
||||
);
|
||||
let login_status = get_login_status(&initial_config);
|
||||
@@ -773,19 +778,24 @@ async fn run_ratatui_app(
|
||||
let provider_filter = vec![config.model_provider_id.clone()];
|
||||
match RolloutRecorder::list_threads(
|
||||
&config,
|
||||
1,
|
||||
None,
|
||||
/*page_size*/ 1,
|
||||
/*cursor*/ None,
|
||||
ThreadSortKey::UpdatedAt,
|
||||
INTERACTIVE_SESSION_SOURCES,
|
||||
Some(provider_filter.as_slice()),
|
||||
&config.model_provider_id,
|
||||
None,
|
||||
/*search_term*/ None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(page) => match page.items.first() {
|
||||
Some(item) => {
|
||||
match resolve_session_thread_id(item.path.as_path(), None).await {
|
||||
match resolve_session_thread_id(
|
||||
item.path.as_path(),
|
||||
/*id_str_if_uuid*/ None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Some(thread_id) => resume_picker::SessionSelection::Fork(
|
||||
resume_picker::SessionTarget {
|
||||
path: item.path.clone(),
|
||||
@@ -868,8 +878,8 @@ async fn run_ratatui_app(
|
||||
};
|
||||
match RolloutRecorder::find_latest_thread_path(
|
||||
&config,
|
||||
1,
|
||||
None,
|
||||
/*page_size*/ 1,
|
||||
/*cursor*/ None,
|
||||
ThreadSortKey::UpdatedAt,
|
||||
INTERACTIVE_SESSION_SOURCES,
|
||||
Some(provider_filter.as_slice()),
|
||||
@@ -878,30 +888,34 @@ async fn run_ratatui_app(
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Some(path)) => match resolve_session_thread_id(path.as_path(), None).await {
|
||||
Some(thread_id) => {
|
||||
resume_picker::SessionSelection::Resume(resume_picker::SessionTarget {
|
||||
path,
|
||||
thread_id,
|
||||
})
|
||||
Ok(Some(path)) => {
|
||||
match resolve_session_thread_id(path.as_path(), /*id_str_if_uuid*/ None).await {
|
||||
Some(thread_id) => {
|
||||
resume_picker::SessionSelection::Resume(resume_picker::SessionTarget {
|
||||
path,
|
||||
thread_id,
|
||||
})
|
||||
}
|
||||
None => {
|
||||
let rollout_path = path.display();
|
||||
error!(
|
||||
"Error reading session metadata from latest rollout: {rollout_path}"
|
||||
);
|
||||
restore();
|
||||
session_log::log_session_end();
|
||||
let _ = tui.terminal.clear();
|
||||
return Ok(AppExitInfo {
|
||||
token_usage: codex_protocol::protocol::TokenUsage::default(),
|
||||
thread_id: None,
|
||||
thread_name: None,
|
||||
update_action: None,
|
||||
exit_reason: ExitReason::Fatal(format!(
|
||||
"Found latest saved session at {rollout_path}, but failed to read its metadata. Run `codex resume` to choose from existing sessions."
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let rollout_path = path.display();
|
||||
error!("Error reading session metadata from latest rollout: {rollout_path}");
|
||||
restore();
|
||||
session_log::log_session_end();
|
||||
let _ = tui.terminal.clear();
|
||||
return Ok(AppExitInfo {
|
||||
token_usage: codex_protocol::protocol::TokenUsage::default(),
|
||||
thread_id: None,
|
||||
thread_name: None,
|
||||
update_action: None,
|
||||
exit_reason: ExitReason::Fatal(format!(
|
||||
"Found latest saved session at {rollout_path}, but failed to read its metadata. Run `codex resume` to choose from existing sessions."
|
||||
)),
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => resume_picker::SessionSelection::StartFresh,
|
||||
}
|
||||
} else if cli.resume_picker {
|
||||
@@ -1222,8 +1236,13 @@ async fn load_config_or_exit(
|
||||
overrides: ConfigOverrides,
|
||||
cloud_requirements: CloudRequirementsLoader,
|
||||
) -> Config {
|
||||
load_config_or_exit_with_fallback_cwd(cli_kv_overrides, overrides, cloud_requirements, None)
|
||||
.await
|
||||
load_config_or_exit_with_fallback_cwd(
|
||||
cli_kv_overrides,
|
||||
overrides,
|
||||
cloud_requirements,
|
||||
/*fallback_cwd*/ None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn load_config_or_exit_with_fallback_cwd(
|
||||
|
||||
@@ -66,7 +66,7 @@ impl RowBuilder {
|
||||
if start < i {
|
||||
self.current_line.push_str(&fragment[start..i]);
|
||||
}
|
||||
self.flush_current_line(true);
|
||||
self.flush_current_line(/*explicit_break*/ true);
|
||||
start = i + ch.len_utf8();
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ impl RowBuilder {
|
||||
|
||||
/// Mark the end of the current logical line (equivalent to pushing a '\n').
|
||||
pub fn end_line(&mut self) {
|
||||
self.flush_current_line(true);
|
||||
self.flush_current_line(/*explicit_break*/ true);
|
||||
}
|
||||
|
||||
/// Drain and return all produced rows.
|
||||
|
||||
@@ -91,7 +91,7 @@ fn main() -> anyhow::Result<()> {
|
||||
into_app_server_cli(inner),
|
||||
arg0_paths,
|
||||
codex_core::config_loader::LoaderOverrides::default(),
|
||||
None,
|
||||
/*remote*/ None,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
|
||||
@@ -87,7 +87,7 @@ impl IndentContext {
|
||||
}
|
||||
|
||||
pub fn render_markdown_text(input: &str) -> Text<'static> {
|
||||
render_markdown_text_with_width(input, None)
|
||||
render_markdown_text_with_width(input, /*width*/ None)
|
||||
}
|
||||
|
||||
/// Render markdown using the current process working directory for local file-link display.
|
||||
@@ -227,8 +227,8 @@ where
|
||||
self.push_line(Line::from("———"));
|
||||
self.needs_newline = true;
|
||||
}
|
||||
Event::Html(html) => self.html(html, false),
|
||||
Event::InlineHtml(html) => self.html(html, true),
|
||||
Event::Html(html) => self.html(html, /*inline*/ false),
|
||||
Event::InlineHtml(html) => self.html(html, /*inline*/ true),
|
||||
Event::FootnoteReference(_) => {}
|
||||
Event::TaskListMarker(_) => {}
|
||||
}
|
||||
@@ -352,8 +352,11 @@ where
|
||||
self.push_blank_line();
|
||||
self.needs_newline = false;
|
||||
}
|
||||
self.indent_stack
|
||||
.push(IndentContext::new(vec![Span::from("> ")], None, false));
|
||||
self.indent_stack.push(IndentContext::new(
|
||||
vec![Span::from("> ")],
|
||||
/*marker*/ None,
|
||||
/*is_list*/ false,
|
||||
));
|
||||
}
|
||||
|
||||
fn end_blockquote(&mut self) {
|
||||
@@ -512,8 +515,11 @@ where
|
||||
let indent_len = if is_ordered { width + 2 } else { width + 1 };
|
||||
vec![Span::from(" ".repeat(indent_len))]
|
||||
};
|
||||
self.indent_stack
|
||||
.push(IndentContext::new(indent_prefix, marker, true));
|
||||
self.indent_stack.push(IndentContext::new(
|
||||
indent_prefix,
|
||||
marker,
|
||||
/*is_list*/ true,
|
||||
));
|
||||
self.needs_newline = false;
|
||||
}
|
||||
|
||||
@@ -538,8 +544,8 @@ where
|
||||
|
||||
self.indent_stack.push(IndentContext::new(
|
||||
vec![indent.unwrap_or_default()],
|
||||
None,
|
||||
false,
|
||||
/*marker*/ None,
|
||||
/*is_list*/ false,
|
||||
));
|
||||
self.needs_newline = true;
|
||||
}
|
||||
@@ -659,7 +665,7 @@ where
|
||||
let was_pending = self.pending_marker_line;
|
||||
|
||||
self.current_initial_indent = self.prefix_spans(was_pending);
|
||||
self.current_subsequent_indent = self.prefix_spans(false);
|
||||
self.current_subsequent_indent = self.prefix_spans(/*pending_marker_line*/ false);
|
||||
self.current_line_style = style;
|
||||
self.current_line_content = Some(line);
|
||||
self.current_line_in_code_block = self.in_code_block;
|
||||
|
||||
@@ -307,7 +307,9 @@ impl ModelMigrationScreen {
|
||||
column.push(
|
||||
Paragraph::new(line.clone())
|
||||
.wrap(Wrap { trim: false })
|
||||
.inset(Insets::tlbr(0, 2, 0, 0)),
|
||||
.inset(Insets::tlbr(
|
||||
/*top*/ 0, /*left*/ 2, /*bottom*/ 0, /*right*/ 0,
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -326,7 +328,12 @@ impl ModelMigrationScreen {
|
||||
column.push(
|
||||
Paragraph::new(line)
|
||||
.wrap(Wrap { trim: false })
|
||||
.inset(Insets::tlbr(0, horizontal_inset, 0, 0)),
|
||||
.inset(Insets::tlbr(
|
||||
/*top*/ 0,
|
||||
horizontal_inset,
|
||||
/*bottom*/ 0,
|
||||
/*right*/ 0,
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -336,7 +343,9 @@ impl ModelMigrationScreen {
|
||||
column.push(
|
||||
Paragraph::new("Choose how you'd like Codex to proceed.")
|
||||
.wrap(Wrap { trim: false })
|
||||
.inset(Insets::tlbr(0, 2, 0, 0)),
|
||||
.inset(Insets::tlbr(
|
||||
/*top*/ 0, /*left*/ 2, /*bottom*/ 0, /*right*/ 0,
|
||||
)),
|
||||
);
|
||||
column.push(Line::from(""));
|
||||
|
||||
@@ -359,7 +368,9 @@ impl ModelMigrationScreen {
|
||||
key_hint::plain(KeyCode::Enter).into(),
|
||||
" to confirm".dim(),
|
||||
])
|
||||
.inset(Insets::tlbr(0, 2, 0, 0)),
|
||||
.inset(Insets::tlbr(
|
||||
/*top*/ 0, /*left*/ 2, /*bottom*/ 0, /*right*/ 0,
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ pub(crate) fn interaction_end(ev: CollabAgentInteractionEndEvent) -> PlainHistor
|
||||
nickname: receiver_agent_nickname.as_deref(),
|
||||
role: receiver_agent_role.as_deref(),
|
||||
},
|
||||
None,
|
||||
/*spawn_request*/ None,
|
||||
);
|
||||
|
||||
let mut details = Vec::new();
|
||||
@@ -244,7 +244,11 @@ pub(crate) fn waiting_begin(ev: CollabWaitingBeginEvent) -> PlainHistoryCell {
|
||||
let receiver_agents = merge_wait_receivers(&receiver_thread_ids, receiver_agents);
|
||||
|
||||
let title = match receiver_agents.as_slice() {
|
||||
[receiver] => title_with_agent("Waiting for", agent_label_from_ref(receiver), None),
|
||||
[receiver] => title_with_agent(
|
||||
"Waiting for",
|
||||
agent_label_from_ref(receiver),
|
||||
/*spawn_request*/ None,
|
||||
),
|
||||
[] => title_text("Waiting for agents"),
|
||||
_ => title_text(format!("Waiting for {} agents", receiver_agents.len())),
|
||||
};
|
||||
@@ -290,7 +294,7 @@ pub(crate) fn close_end(ev: CollabCloseEndEvent) -> PlainHistoryCell {
|
||||
nickname: receiver_agent_nickname.as_deref(),
|
||||
role: receiver_agent_role.as_deref(),
|
||||
},
|
||||
None,
|
||||
/*spawn_request*/ None,
|
||||
),
|
||||
Vec::new(),
|
||||
)
|
||||
@@ -313,7 +317,7 @@ pub(crate) fn resume_begin(ev: CollabResumeBeginEvent) -> PlainHistoryCell {
|
||||
nickname: receiver_agent_nickname.as_deref(),
|
||||
role: receiver_agent_role.as_deref(),
|
||||
},
|
||||
None,
|
||||
/*spawn_request*/ None,
|
||||
),
|
||||
Vec::new(),
|
||||
)
|
||||
@@ -337,7 +341,7 @@ pub(crate) fn resume_end(ev: CollabResumeEndEvent) -> PlainHistoryCell {
|
||||
nickname: receiver_agent_nickname.as_deref(),
|
||||
role: receiver_agent_role.as_deref(),
|
||||
},
|
||||
None,
|
||||
/*spawn_request*/ None,
|
||||
),
|
||||
vec![status_summary_line(&status)],
|
||||
)
|
||||
|
||||
@@ -137,19 +137,19 @@ impl KeyboardHandler for AuthModeWidget {
|
||||
|
||||
match key_event.code {
|
||||
KeyCode::Up | KeyCode::Char('k') => {
|
||||
self.move_highlight(-1);
|
||||
self.move_highlight(/*delta*/ -1);
|
||||
}
|
||||
KeyCode::Down | KeyCode::Char('j') => {
|
||||
self.move_highlight(1);
|
||||
self.move_highlight(/*delta*/ 1);
|
||||
}
|
||||
KeyCode::Char('1') => {
|
||||
self.select_option_by_index(0);
|
||||
self.select_option_by_index(/*index*/ 0);
|
||||
}
|
||||
KeyCode::Char('2') => {
|
||||
self.select_option_by_index(1);
|
||||
self.select_option_by_index(/*index*/ 1);
|
||||
}
|
||||
KeyCode::Char('3') => {
|
||||
self.select_option_by_index(2);
|
||||
self.select_option_by_index(/*index*/ 2);
|
||||
}
|
||||
KeyCode::Enter => {
|
||||
let sign_in_state = { (*self.sign_in_state.read().unwrap()).clone() };
|
||||
|
||||
@@ -56,7 +56,7 @@ impl WidgetRef for &TrustDirectoryWidget {
|
||||
"Do you trust the contents of this directory? Working with untrusted contents comes with higher risk of prompt injection.".to_string(),
|
||||
)
|
||||
.wrap(Wrap { trim: true })
|
||||
.inset(Insets::tlbr(0, 2, 0, 0)),
|
||||
.inset(Insets::tlbr(/*top*/ 0, /*left*/ 2, /*bottom*/ 0, /*right*/ 0)),
|
||||
);
|
||||
column.push("");
|
||||
|
||||
@@ -80,7 +80,9 @@ impl WidgetRef for &TrustDirectoryWidget {
|
||||
Paragraph::new(error.to_string())
|
||||
.red()
|
||||
.wrap(Wrap { trim: true })
|
||||
.inset(Insets::tlbr(0, 2, 0, 0)),
|
||||
.inset(Insets::tlbr(
|
||||
/*top*/ 0, /*left*/ 2, /*bottom*/ 0, /*right*/ 0,
|
||||
)),
|
||||
);
|
||||
column.push("");
|
||||
}
|
||||
@@ -95,7 +97,9 @@ impl WidgetRef for &TrustDirectoryWidget {
|
||||
" to continue".dim()
|
||||
},
|
||||
])
|
||||
.inset(Insets::tlbr(0, 2, 0, 0)),
|
||||
.inset(Insets::tlbr(
|
||||
/*top*/ 0, /*left*/ 2, /*bottom*/ 0, /*right*/ 0,
|
||||
)),
|
||||
);
|
||||
|
||||
column.render(area, buf);
|
||||
|
||||
@@ -457,7 +457,7 @@ impl TranscriptOverlay {
|
||||
pub(crate) fn new(transcript_cells: Vec<Arc<dyn HistoryCell>>) -> Self {
|
||||
Self {
|
||||
view: PagerView::new(
|
||||
Self::render_cells(&transcript_cells, None),
|
||||
Self::render_cells(&transcript_cells, /*highlight_cell*/ None),
|
||||
"T R A N S C R I P T".to_string(),
|
||||
usize::MAX,
|
||||
),
|
||||
@@ -495,7 +495,9 @@ impl TranscriptOverlay {
|
||||
if !c.is_stream_continuation() && i > 0 {
|
||||
cell_renderable = Box::new(InsetRenderable::new(
|
||||
cell_renderable,
|
||||
Insets::tlbr(1, 0, 0, 0),
|
||||
Insets::tlbr(
|
||||
/*top*/ 1, /*left*/ 0, /*bottom*/ 0, /*right*/ 0,
|
||||
),
|
||||
));
|
||||
}
|
||||
v.push(cell_renderable);
|
||||
@@ -528,8 +530,12 @@ impl TranscriptOverlay {
|
||||
{
|
||||
// The tail was rendered as the only entry, so it lacks a top
|
||||
// inset; add one now that it follows a committed cell.
|
||||
Box::new(InsetRenderable::new(tail, Insets::tlbr(1, 0, 0, 0)))
|
||||
as Box<dyn Renderable>
|
||||
Box::new(InsetRenderable::new(
|
||||
tail,
|
||||
Insets::tlbr(
|
||||
/*top*/ 1, /*left*/ 0, /*bottom*/ 0, /*right*/ 0,
|
||||
),
|
||||
)) as Box<dyn Renderable>
|
||||
} else {
|
||||
tail
|
||||
};
|
||||
@@ -649,7 +655,12 @@ impl TranscriptOverlay {
|
||||
let paragraph = Paragraph::new(Text::from(lines)).wrap(Wrap { trim: false });
|
||||
let mut renderable: Box<dyn Renderable> = Box::new(CachedRenderable::new(paragraph));
|
||||
if has_prior_cells && !is_stream_continuation {
|
||||
renderable = Box::new(InsetRenderable::new(renderable, Insets::tlbr(1, 0, 0, 0)));
|
||||
renderable = Box::new(InsetRenderable::new(
|
||||
renderable,
|
||||
Insets::tlbr(
|
||||
/*top*/ 1, /*left*/ 0, /*bottom*/ 0, /*right*/ 0,
|
||||
),
|
||||
));
|
||||
}
|
||||
renderable
|
||||
}
|
||||
@@ -721,7 +732,7 @@ impl StaticOverlay {
|
||||
|
||||
pub(crate) fn with_renderables(renderables: Vec<Box<dyn Renderable>>, title: String) -> Self {
|
||||
Self {
|
||||
view: PagerView::new(renderables, title, 0),
|
||||
view: PagerView::new(renderables, title, /*scroll_offset*/ 0),
|
||||
is_done: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,13 @@ impl ComposerInput {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let sender = AppEventSender::new(tx.clone());
|
||||
// `enhanced_keys_supported=true` enables Shift+Enter newline hint/behavior.
|
||||
let inner = ChatComposer::new(true, sender, true, "Compose new task".to_string(), false);
|
||||
let inner = ChatComposer::new(
|
||||
/*has_input_focus*/ true,
|
||||
sender,
|
||||
/*enhanced_keys_supported*/ true,
|
||||
"Compose new task".to_string(),
|
||||
/*disable_paste_burst*/ false,
|
||||
);
|
||||
Self { inner, _tx: tx, rx }
|
||||
}
|
||||
|
||||
@@ -80,7 +86,7 @@ impl ComposerInput {
|
||||
|
||||
/// Clear any previously set custom hint items and restore the default hints.
|
||||
pub fn clear_hint_items(&mut self) {
|
||||
self.inner.set_footer_hint_override(None);
|
||||
self.inner.set_footer_hint_override(/*items*/ None);
|
||||
}
|
||||
|
||||
/// Desired height (in rows) for a given width.
|
||||
|
||||
@@ -167,7 +167,7 @@ async fn run_session_picker(
|
||||
INTERACTIVE_SESSION_SOURCES,
|
||||
Some(provider_filter.as_slice()),
|
||||
request.default_provider.as_str(),
|
||||
None,
|
||||
/*search_term*/ None,
|
||||
)
|
||||
.await;
|
||||
let _ = tx.send(BackgroundEvent::PageLoaded {
|
||||
@@ -416,7 +416,13 @@ impl PickerState {
|
||||
let path = row.path.clone();
|
||||
let thread_id = match row.thread_id {
|
||||
Some(thread_id) => Some(thread_id),
|
||||
None => crate::resolve_session_thread_id(path.as_path(), None).await,
|
||||
None => {
|
||||
crate::resolve_session_thread_id(
|
||||
path.as_path(),
|
||||
/*id_str_if_uuid*/ None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
};
|
||||
if let Some(thread_id) = thread_id {
|
||||
return Ok(Some(self.action.selection(path, thread_id)));
|
||||
@@ -1255,14 +1261,14 @@ fn calculate_column_metrics(rows: &[Row], include_cwd: bool) -> ColumnMetrics {
|
||||
let created = format_created_label(row);
|
||||
let updated = format_updated_label(row);
|
||||
let branch_raw = row.git_branch.clone().unwrap_or_default();
|
||||
let branch = right_elide(&branch_raw, 24);
|
||||
let branch = right_elide(&branch_raw, /*max*/ 24);
|
||||
let cwd = if include_cwd {
|
||||
let cwd_raw = row
|
||||
.cwd
|
||||
.as_ref()
|
||||
.map(|p| display_path_for(p, std::path::Path::new("/")))
|
||||
.unwrap_or_default();
|
||||
right_elide(&cwd_raw, 24)
|
||||
right_elide(&cwd_raw, /*max*/ 24)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ pub(crate) fn selection_option_row(
|
||||
label: String,
|
||||
is_selected: bool,
|
||||
) -> Box<dyn Renderable> {
|
||||
selection_option_row_with_dim(index, label, is_selected, false)
|
||||
selection_option_row_with_dim(index, label, is_selected, /*dim*/ false)
|
||||
}
|
||||
|
||||
pub(crate) fn selection_option_row_with_dim(
|
||||
|
||||
@@ -209,7 +209,7 @@ impl StatusIndicatorWidget {
|
||||
let opts = RtOptions::new(usize::from(width))
|
||||
.initial_indent(Line::from(DETAILS_PREFIX.dim()))
|
||||
.subsequent_indent(Line::from(Span::from(" ".repeat(prefix_width)).dim()))
|
||||
.break_words(true);
|
||||
.break_words(/*break_words*/ true);
|
||||
|
||||
let mut out = word_wrap_lines(details.lines().map(|line| vec![line.dim()]), opts);
|
||||
|
||||
|
||||
@@ -167,13 +167,16 @@ impl PlanStreamController {
|
||||
}
|
||||
|
||||
self.state.clear();
|
||||
self.emit(out_lines, true)
|
||||
self.emit(out_lines, /*include_bottom_padding*/ true)
|
||||
}
|
||||
|
||||
/// Step animation: commit at most one queued line and handle end-of-drain cleanup.
|
||||
pub(crate) fn on_commit_tick(&mut self) -> (Option<Box<dyn HistoryCell>>, bool) {
|
||||
let step = self.state.step();
|
||||
(self.emit(step, false), self.state.is_idle())
|
||||
(
|
||||
self.emit(step, /*include_bottom_padding*/ false),
|
||||
self.state.is_idle(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Step animation: commit at most `max_lines` queued lines.
|
||||
@@ -185,7 +188,10 @@ impl PlanStreamController {
|
||||
max_lines: usize,
|
||||
) -> (Option<Box<dyn HistoryCell>>, bool) {
|
||||
let step = self.state.drain_n(max_lines.max(1));
|
||||
(self.emit(step, false), self.state.is_idle())
|
||||
(
|
||||
self.emit(step, /*include_bottom_padding*/ false),
|
||||
self.state.is_idle(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the current number of queued plan lines waiting to be displayed.
|
||||
|
||||
@@ -242,7 +242,13 @@ impl Renderable for ThemePreviewWideRenderable {
|
||||
}
|
||||
|
||||
fn render(&self, area: Rect, buf: &mut Buffer) {
|
||||
render_preview(area, buf, &WIDE_PREVIEW_ROWS, true, WIDE_PREVIEW_LEFT_INSET);
|
||||
render_preview(
|
||||
area,
|
||||
buf,
|
||||
&WIDE_PREVIEW_ROWS,
|
||||
/*center_vertically*/ true,
|
||||
WIDE_PREVIEW_LEFT_INSET,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +258,13 @@ impl Renderable for ThemePreviewNarrowRenderable {
|
||||
}
|
||||
|
||||
fn render(&self, area: Rect, buf: &mut Buffer) {
|
||||
render_preview(area, buf, &NARROW_PREVIEW_ROWS, false, 0);
|
||||
render_preview(
|
||||
area,
|
||||
buf,
|
||||
&NARROW_PREVIEW_ROWS,
|
||||
/*center_vertically*/ false,
|
||||
/*left_inset*/ 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,7 +286,7 @@ fn theme_picker_subtitle(codex_home: Option<&Path>, terminal_width: Option<u16>)
|
||||
let themes_dir = codex_home.map(|home| home.join("themes"));
|
||||
let themes_dir_display = themes_dir
|
||||
.as_deref()
|
||||
.map(|path| format_directory_display(path, None));
|
||||
.map(|path| format_directory_display(path, /*max_width*/ None));
|
||||
let available_width = subtitle_available_width(terminal_width);
|
||||
|
||||
if let Some(path) = themes_dir_display
|
||||
|
||||
@@ -464,7 +464,7 @@ fn is_domain_label(label: &str) -> bool {
|
||||
pub(crate) fn url_preserving_wrap_options<'a>(opts: RtOptions<'a>) -> RtOptions<'a> {
|
||||
opts.word_separator(textwrap::WordSeparator::AsciiSpace)
|
||||
.word_splitter(textwrap::WordSplitter::Custom(split_non_url_word))
|
||||
.break_words(false)
|
||||
.break_words(/*break_words*/ false)
|
||||
}
|
||||
|
||||
/// Custom `textwrap::WordSplitter` callback. Returns empty (no split
|
||||
|
||||
Reference in New Issue
Block a user