Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions codex-rs/core/src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ pub enum Feature {
ShellSnapshot,
/// Experimental TUI v2 (viewport) implementation.
Tui2,
/// Experimental entertainment mode for the status shimmer.
Entertainment,
/// Enforce UTF8 output in Powershell.
PowershellUtf8,
}
Expand Down Expand Up @@ -380,4 +382,10 @@ pub const FEATURES: &[FeatureSpec] = &[
stage: Stage::Experimental,
default_enabled: false,
},
FeatureSpec {
id: Feature::Entertainment,
key: "entertainment",
stage: Stage::Experimental,
default_enabled: false,
},
];
29 changes: 29 additions & 0 deletions codex-rs/tui/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::bottom_pane::ApprovalRequest;
use crate::chatwidget::ChatWidget;
use crate::chatwidget::ExternalEditorState;
use crate::diff_render::DiffSummary;
use crate::entertainment;
use crate::exec_command::strip_bash_lc_and_escape;
use crate::external_editor;
use crate::file_search::FileSearchManager;
Expand Down Expand Up @@ -61,6 +62,7 @@ use std::thread;
use std::time::Duration;
use tokio::select;
use tokio::sync::mpsc::unbounded_channel;
use tracing::info;

#[cfg(not(debug_assertions))]
use crate::history_cell::UpdateAvailableHistoryCell;
Expand Down Expand Up @@ -735,6 +737,33 @@ impl App {
));
tui.frame_requester().schedule_frame();
}
AppEvent::GenerateEntertainmentTexts { prompt } => {
info!(
prompt_len = prompt.len(),
"received request to generate entertainment texts"
);
let app_event_tx = self.app_event_tx.clone();
let server = Arc::clone(&self.server);
let config = self.config.clone();
tokio::spawn(async move {
match entertainment::generate_entertainment_texts(server, config, prompt).await
{
Ok(texts) => {
info!(
texts_len = texts.len(),
"entertainment text generation completed"
);
app_event_tx.send(AppEvent::EntertainmentTextsGenerated { texts });
}
Err(err) => {
tracing::warn!("entertainment text generation failed: {err}");
}
}
});
}
AppEvent::EntertainmentTextsGenerated { texts } => {
self.chat_widget.update_entertainment_texts(texts);
}
AppEvent::StartFileSearch(query) => {
if !query.is_empty() {
self.file_search.on_user_query(query);
Expand Down
8 changes: 8 additions & 0 deletions codex-rs/tui/src/app_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ pub(crate) enum AppEvent {
matches: Vec<FileMatch>,
},

GenerateEntertainmentTexts {
prompt: String,
},

EntertainmentTextsGenerated {
texts: Vec<String>,
},

/// Result of refreshing rate limits
RateLimitSnapshotFetched(RateLimitSnapshot),

Expand Down
31 changes: 31 additions & 0 deletions codex-rs/tui/src/bottom_pane/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::path::PathBuf;
use crate::app_event_sender::AppEventSender;
use crate::bottom_pane::queued_user_messages::QueuedUserMessages;
use crate::bottom_pane::unified_exec_footer::UnifiedExecFooter;
use crate::entertainment::EntertainmentArcStore;
use crate::render::renderable::FlexRenderable;
use crate::render::renderable::Renderable;
use crate::render::renderable::RenderableItem;
Expand Down Expand Up @@ -78,6 +79,8 @@ pub(crate) struct BottomPane {
ctrl_c_quit_hint: bool,
esc_backtrack_hint: bool,
animations_enabled: bool,
entertainment_enabled: bool,
entertainment_arcs: EntertainmentArcStore,

/// Inline status indicator shown above the composer while a task is running.
status: Option<StatusIndicatorWidget>,
Expand All @@ -97,6 +100,7 @@ pub(crate) struct BottomPaneParams {
pub(crate) placeholder_text: String,
pub(crate) disable_paste_burst: bool,
pub(crate) animations_enabled: bool,
pub(crate) entertainment_enabled: bool,
pub(crate) skills: Option<Vec<SkillMetadata>>,
}

Expand All @@ -110,6 +114,7 @@ impl BottomPane {
placeholder_text,
disable_paste_burst,
animations_enabled,
entertainment_enabled,
skills,
} = params;
let mut composer = ChatComposer::new(
Expand All @@ -134,6 +139,8 @@ impl BottomPane {
queued_user_messages: QueuedUserMessages::new(),
esc_backtrack_hint: false,
animations_enabled,
entertainment_enabled,
entertainment_arcs: EntertainmentArcStore::new(entertainment_enabled),
context_window_percent: None,
context_window_used_tokens: None,
}
Expand Down Expand Up @@ -353,7 +360,9 @@ impl BottomPane {
self.app_event_tx.clone(),
self.frame_requester.clone(),
self.animations_enabled,
self.entertainment_enabled,
));
self.apply_entertainment_arcs();
}
if let Some(status) = self.status.as_mut() {
status.set_interrupt_hint_visible(true);
Expand All @@ -379,7 +388,16 @@ impl BottomPane {
self.app_event_tx.clone(),
self.frame_requester.clone(),
self.animations_enabled,
self.entertainment_enabled,
));
self.apply_entertainment_arcs();
self.request_redraw();
}
}

pub(crate) fn update_entertainment_texts(&mut self, texts: Vec<String>) {
self.entertainment_arcs.push(texts, self.status.as_mut());
if self.status.is_some() {
self.request_redraw();
}
}
Expand All @@ -391,6 +409,12 @@ impl BottomPane {
}
}

fn apply_entertainment_arcs(&mut self) {
if let Some(status) = self.status.as_mut() {
self.entertainment_arcs.apply_to(status);
}
}

pub(crate) fn set_context_window(&mut self, percent: Option<i64>, used_tokens: Option<i64>) {
if self.context_window_percent == percent && self.context_window_used_tokens == used_tokens
{
Expand Down Expand Up @@ -636,6 +660,7 @@ mod tests {
placeholder_text: "Ask Codex to do anything".to_string(),
disable_paste_burst: false,
animations_enabled: true,
entertainment_enabled: false,
skills: Some(Vec::new()),
});
pane.push_approval_request(exec_request(), &features);
Expand All @@ -659,6 +684,7 @@ mod tests {
placeholder_text: "Ask Codex to do anything".to_string(),
disable_paste_burst: false,
animations_enabled: true,
entertainment_enabled: false,
skills: Some(Vec::new()),
});

Expand Down Expand Up @@ -693,6 +719,7 @@ mod tests {
placeholder_text: "Ask Codex to do anything".to_string(),
disable_paste_burst: false,
animations_enabled: true,
entertainment_enabled: false,
skills: Some(Vec::new()),
});

Expand Down Expand Up @@ -760,6 +787,7 @@ mod tests {
placeholder_text: "Ask Codex to do anything".to_string(),
disable_paste_burst: false,
animations_enabled: true,
entertainment_enabled: false,
skills: Some(Vec::new()),
});

Expand Down Expand Up @@ -787,6 +815,7 @@ mod tests {
placeholder_text: "Ask Codex to do anything".to_string(),
disable_paste_burst: false,
animations_enabled: true,
entertainment_enabled: false,
skills: Some(Vec::new()),
});

Expand Down Expand Up @@ -818,6 +847,7 @@ mod tests {
placeholder_text: "Ask Codex to do anything".to_string(),
disable_paste_burst: false,
animations_enabled: true,
entertainment_enabled: false,
skills: Some(Vec::new()),
});

Expand Down Expand Up @@ -846,6 +876,7 @@ mod tests {
placeholder_text: "Ask Codex to do anything".to_string(),
disable_paste_burst: false,
animations_enabled: true,
entertainment_enabled: false,
skills: Some(Vec::new()),
});

Expand Down
Loading
Loading