fix(tui): clear full history region (incl. padding) each frame to eliminate rare margin artifacts and ensure background is fully painted

This commit is contained in:
James Peter
2025-08-24 00:57:37 +10:00
parent baa1d52e2b
commit ffd1120234
25 changed files with 127 additions and 94 deletions
+60 -4
View File
@@ -5,6 +5,9 @@ set -euo pipefail
# Change to the Rust project root directory
cd codex-rs
# Compute repository root (one level up from codex-rs)
REPO_ROOT="$(cd .. && pwd)"
# Use dev-fast profile by default for quick iteration
# Can override with: PROFILE=release ./build-fast.sh
PROFILE="${PROFILE:-dev-fast}"
@@ -57,7 +60,7 @@ if command -v rustup >/dev/null 2>&1; then
if [ -n "$TOOLCHAIN" ]; then
# Ensure toolchain is installed; if not, attempt install quietly
if ! rustup toolchain list | awk '{print $1}' | grep -qx "$TOOLCHAIN"; then
if ! rustup which rustc --toolchain "$TOOLCHAIN" >/dev/null 2>&1; then
echo "rustup: installing toolchain $TOOLCHAIN ..."
rustup toolchain install "$TOOLCHAIN" >/dev/null 2>&1 || true
fi
@@ -73,16 +76,44 @@ else
exit 1
fi
# Optional trace mode for diagnosing environment differences
if [ "${TRACE_BUILD:-}" = "1" ]; then
echo "--- TRACE_BUILD environment ---"
echo "whoami: $(whoami)"
echo "pwd: $(pwd)"
echo "SHELL: ${SHELL:-}"
bash --version | head -n1 || true
if [ -n "${TOOLCHAIN:-}" ]; then
echo "TOOLCHAIN: ${TOOLCHAIN}"
rustup run "$TOOLCHAIN" rustc -vV || true
rustup run "$TOOLCHAIN" cargo -vV || true
fi
echo "Filtered env (CARGO|RUST*|PROFILE|CODE_HOME|CODEX_HOME):"
env | egrep '^(CARGO|RUST|RUSTUP|PROFILE|CODE_HOME|CODEX_HOME)=' | sort || true
echo "--------------------------------"
fi
# Build for native target (no --target flag) for maximum speed
# This reuses the host stdlib and normal cache
# Build with or without --locked based on lockfile validity
${USE_CARGO} build ${USE_LOCKED} --profile "${PROFILE}" --bin code --bin code-tui --bin code-exec
# Merge cargo's stderr into stdout so CI/harnesses that only capture stdout
# still show all compilation lines and warnings just like an interactive shell.
${USE_CARGO} build ${USE_LOCKED} --profile "${PROFILE}" --bin code --bin code-tui --bin code-exec 2>&1
# Check if build succeeded
if [ $? -eq 0 ]; then
echo "✅ Build successful!"
echo "Binary location: ${BIN_PATH}"
# Compute absolute path and SHA256 for clarity
ABS_BIN_PATH="$(cd "$(dirname "${BIN_PATH}")" && pwd)/$(basename "${BIN_PATH}")"
if command -v shasum >/dev/null 2>&1; then
BIN_SHA="$(shasum -a 256 "${ABS_BIN_PATH}" | awk '{print $1}')"
elif command -v sha256sum >/dev/null 2>&1; then
BIN_SHA="$(sha256sum "${ABS_BIN_PATH}" | awk '{print $1}')"
else
BIN_SHA=""
fi
# Keep old symlink locations working for compatibility
# Create symlink in target/release for npm wrapper expectations
@@ -104,7 +135,12 @@ if [ $? -eq 0 ]; then
echo "✅ Symlinks updated"
echo ""
echo "You can now run: code"
echo "You can run the CLI directly:"
if [ -n "$BIN_SHA" ]; then
echo " ${ABS_BIN_PATH} (sha256: ${BIN_SHA})"
else
echo " ${ABS_BIN_PATH}"
fi
echo "Binary size: $(du -h ${BIN_PATH} | cut -f1)"
echo ""
echo "Build profile: ${PROFILE}"
@@ -113,6 +149,26 @@ if [ $? -eq 0 ]; then
echo " → For production build, use: PROFILE=release-prod ./build-fast.sh"
fi
# PATH guidance and collision warning
REPO_BIN_DIR="${REPO_ROOT}/codex-cli/bin"
CODE_PATH_ON_PATH="$(command -v code 2>/dev/null || true)"
if [ -n "$CODE_PATH_ON_PATH" ] && [[ "$CODE_PATH_ON_PATH" != "${REPO_BIN_DIR}/code" && "$CODE_PATH_ON_PATH" != *"/codex-cli/bin/"* ]]; then
echo ""
echo "⚠️ PATH notice: 'code' currently resolves to: $CODE_PATH_ON_PATH"
echo " That is likely Visual Studio Code, not this CLI."
echo " To use this binary via 'code', prepend the repo bin directory to PATH:"
echo " export PATH=\"${REPO_BIN_DIR}:\$PATH\""
echo " Then verify with:"
echo " which code"
fi
if [ "${TRACE_BUILD:-}" = "1" ] && [ -n "$BIN_SHA" ]; then
echo "--- TRACE_BUILD artifact ---"
echo "ABS_BIN_PATH: ${ABS_BIN_PATH}"
echo "SHA256: ${BIN_SHA}"
echo "--------------------------------"
fi
# If lockfile was out of date, remind user
if [ -z "$USE_LOCKED" ]; then
echo ""
@@ -122,4 +178,4 @@ if [ $? -eq 0 ]; then
else
echo "❌ Build failed"
exit 1
fi
fi
+1 -1
View File
@@ -1059,7 +1059,7 @@ impl BrowserManager {
// Skip redundant overrides within a short window to prevent flash
{
let mut guard = self.last_metrics_applied.lock().await;
let guard = self.last_metrics_applied.lock().await;
if let Some((lw, lh, ldpr, lmob, ts)) = *guard {
let same = lw == w && lh == h && (ldpr - dpr).abs() < 0.001 && lmob == mob;
let recent = ts.elapsed() < std::time::Duration::from_secs(30);
+4 -2
View File
@@ -131,6 +131,7 @@ impl Page {
}
/// (NEW) Injects the script to prevent new tabs from opening and redirect them to the current tab.
#[allow(dead_code)]
async fn inject_tab_interception_script(cdp_page: &Arc<CdpPage>) -> Result<()> {
// The comprehensive script ported from browser_session.ts
let script = r#"
@@ -485,7 +486,7 @@ impl Page {
.await;
match retry_attempt {
Ok(Ok(resp)) => Ok(resp.result),
Ok(Err(err2)) => {
Ok(Err(_)) => {
// Last resort for visible pages: try from_surface(true).
// This can flash; we only do it after exhausting the safer path to prevent permanent failures.
debug!(
@@ -557,7 +558,7 @@ impl Page {
.await;
match retry_attempt {
Ok(Ok(resp)) => Ok(resp.result),
Ok(Err(e)) => {
Ok(Err(_)) => {
// Final fallback with from_surface(true) even though visible (see doc rationale)
debug!(
"Second attempt with from_surface(false) failed while visible; attempting final from_surface(true)"
@@ -607,6 +608,7 @@ impl Page {
}
/// Check and fix viewport scaling issues before taking screenshots
#[allow(dead_code)]
async fn check_and_fix_scaling(&self) -> Result<()> {
// Never touch viewport metrics for external Chrome connections.
// Changing device metrics on a user's Chrome causes a visible flash
+3
View File
@@ -394,17 +394,20 @@ impl ModelClient {
}
/// Returns the currently configured model slug.
#[allow(dead_code)]
pub fn get_model(&self) -> String {
self.config.model.clone()
}
/// Returns the currently configured model family.
#[allow(dead_code)]
pub fn get_model_family(&self) -> ModelFamily {
self.config.model_family.clone()
}
// duplicate of earlier helpers removed during merge cleanup
#[allow(dead_code)]
pub fn get_auth_manager(&self) -> Option<Arc<AuthManager>> {
self.auth_manager.clone()
}
+11 -11
View File
@@ -17,10 +17,10 @@ use base64::Engine;
use codex_apply_patch::ApplyPatchAction;
use codex_apply_patch::MaybeApplyPatchVerified;
use codex_apply_patch::maybe_parse_apply_patch_verified;
use codex_login::AuthManager;
use codex_protocol::protocol::ConversationHistoryResponseEvent;
// unused: AuthManager
// unused: ConversationHistoryResponseEvent
use codex_protocol::protocol::TurnAbortReason;
use codex_protocol::protocol::TurnAbortedEvent;
// unused: TurnAbortedEvent
use futures::prelude::*;
use mcp_types::CallToolResult;
use serde::Serialize;
@@ -549,7 +549,7 @@ pub(crate) struct Session {
approval_policy: AskForApproval,
sandbox_policy: SandboxPolicy,
shell_environment_policy: ShellEnvironmentPolicy,
writable_roots: Vec<PathBuf>,
_writable_roots: Vec<PathBuf>,
disable_response_storage: bool,
tools_config: ToolsConfig,
@@ -580,8 +580,9 @@ pub(crate) struct Session {
}
impl Session {
#[allow(dead_code)]
pub(crate) fn get_writable_roots(&self) -> &[PathBuf] {
&self.writable_roots
&self._writable_roots
}
pub(crate) fn get_approval_policy(&self) -> AskForApproval {
@@ -903,7 +904,7 @@ impl Session {
const MAX_STREAM_OUTPUT: usize = 5 * 1024; // 5KiB
let stdout = stdout.text.chars().take(MAX_STREAM_OUTPUT).collect();
let stderr = stderr.text.chars().take(MAX_STREAM_OUTPUT).collect();
let formatted_output = format_exec_output_str(output);
// Precompute formatted output if needed in future for logging/pretty UI.
let msg = if is_apply_patch {
EventMsg::PatchApplyEnd(PatchApplyEndEvent {
@@ -1279,7 +1280,7 @@ impl AgentAgent {
}
}
fn abort(self, reason: TurnAbortReason) {
fn abort(self, _reason: TurnAbortReason) {
// TOCTOU?
if !self.handle.is_finished() {
self.handle.abort();
@@ -1483,7 +1484,7 @@ async fn submission_loop(
sandbox_policy,
shell_environment_policy: config.shell_environment_policy.clone(),
cwd,
writable_roots,
_writable_roots: writable_roots,
mcp_connection_manager,
agents: config.agents.clone(),
notify,
@@ -2532,8 +2533,8 @@ async fn handle_browser_cleanup(
sub_id: String,
call_id: String,
) -> ResponseInputItem {
let sess_clone = sess;
let call_id_clone = call_id.clone();
let sess_clone = sess;
execute_custom_tool(
sess,
&sub_id,
@@ -3132,7 +3133,7 @@ async fn handle_wait_for_agent(
let agents = manager.list_agents(None, Some(batch_id.clone()), false);
// Separate terminal vs non-terminal agents
let mut completed_agents: Vec<_> = agents
let completed_agents: Vec<_> = agents
.iter()
.filter(|t| {
matches!(
@@ -4085,7 +4086,6 @@ async fn handle_browser_open(
// Parse arguments as JSON for the event
let params = serde_json::from_str(&arguments).ok();
let sess_clone = sess;
let arguments_clone = arguments.clone();
let call_id_clone = call_id.clone();
+3 -3
View File
@@ -31,14 +31,14 @@ pub struct NewConversation {
/// maintaining them in memory.
pub struct ConversationManager {
conversations: Arc<RwLock<HashMap<Uuid, Arc<CodexConversation>>>>,
auth_manager: Arc<AuthManager>,
_auth_manager: Arc<AuthManager>,
}
impl ConversationManager {
pub fn new(auth_manager: Arc<AuthManager>) -> Self {
Self {
conversations: Arc::new(RwLock::new(HashMap::new())),
auth_manager,
_auth_manager: auth_manager,
}
}
@@ -121,7 +121,7 @@ impl ConversationManager {
config: Config,
) -> CodexResult<NewConversation> {
// Compute the prefix up to the cut point.
let truncated_history =
let _truncated_history =
truncate_after_dropping_last_messages(conversation_history, num_messages_to_drop);
// Spawn a new conversation with the computed initial history.
-1
View File
@@ -26,7 +26,6 @@ use std::time::Duration;
use tokio::fs;
use tokio::io::AsyncReadExt;
use uuid::Uuid;
use fs2::FileExt;
use crate::config::Config;
use crate::config_types::HistoryPersistence;
+2 -9
View File
@@ -15,9 +15,7 @@ use crate::model_family::ModelFamily;
use crate::plan_tool::PLAN_TOOL;
use crate::protocol::AskForApproval;
use crate::protocol::SandboxPolicy;
use crate::tool_apply_patch::ApplyPatchToolType;
use crate::tool_apply_patch::create_apply_patch_freeform_tool;
use crate::tool_apply_patch::create_apply_patch_json_tool;
// apply_patch tools are not currently surfaced; keep imports out to avoid warnings.
#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct ResponsesApiTool {
@@ -46,6 +44,7 @@ pub struct FreeformToolFormat {
/// When serialized as JSON, this produces a valid "Tool" in the OpenAI
/// Responses API.
#[allow(dead_code)]
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(tag = "type")]
pub(crate) enum OpenAiTool {
@@ -88,12 +87,6 @@ impl ToolsConfig {
}
}
let apply_patch_tool_type = match model_family.apply_patch_tool_type {
Some(ApplyPatchToolType::Freeform) => Some(ApplyPatchToolType::Freeform),
Some(ApplyPatchToolType::Function) => Some(ApplyPatchToolType::Function),
None => Some(ApplyPatchToolType::Freeform),
};
Self {
shell_type,
plan_tool: include_plan_tool,
+2
View File
@@ -22,6 +22,7 @@ pub enum ApplyPatchToolType {
/// Returns a custom tool that can be used to edit files. Well-suited for GPT-5 models
/// https://platform.openai.com/docs/guides/function-calling#custom-tools
#[allow(dead_code)]
pub(crate) fn create_apply_patch_freeform_tool() -> OpenAiTool {
OpenAiTool::Freeform(FreeformTool {
name: "apply_patch".to_string(),
@@ -55,6 +56,7 @@ eof_line: "*** End of File" LF
}
/// Returns a json tool that can be used to edit files. Should only be used with gpt-oss models
#[allow(dead_code)]
pub(crate) fn create_apply_patch_json_tool() -> OpenAiTool {
let mut properties = BTreeMap::new();
properties.insert(
@@ -21,7 +21,6 @@ use codex_core::protocol::PatchApplyBeginEvent;
use codex_core::protocol::PatchApplyEndEvent;
use codex_core::protocol::SessionConfiguredEvent;
use codex_core::protocol::TaskCompleteEvent;
use codex_protocol::protocol::TurnAbortReason;
use codex_core::protocol::TurnDiffEvent;
use owo_colors::OwoColorize;
use owo_colors::Style;
@@ -1,7 +1,6 @@
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use codex_core::CodexConversation;
use codex_core::ConversationManager;
@@ -31,16 +30,11 @@ use codex_core::protocol::InputItem as CoreInputItem;
use codex_core::protocol::Op;
use codex_core::protocol as core_protocol;
use codex_login::AuthManager;
use codex_login::CLIENT_ID;
use codex_login::ServerOptions as LoginServerOptions;
use codex_login::ShutdownHandle;
use codex_login::run_login_server;
use codex_protocol::mcp_protocol::APPLY_PATCH_APPROVAL_METHOD;
use codex_protocol::mcp_protocol::AddConversationListenerParams;
use codex_protocol::mcp_protocol::AddConversationSubscriptionResponse;
use codex_protocol::mcp_protocol::ApplyPatchApprovalParams;
use codex_protocol::mcp_protocol::ApplyPatchApprovalResponse;
use codex_protocol::mcp_protocol::AuthStatusChangeNotification;
use codex_protocol::mcp_protocol::ClientRequest;
use codex_protocol::mcp_protocol::ConversationId;
use codex_protocol::mcp_protocol::EXEC_COMMAND_APPROVAL_METHOD;
@@ -49,9 +43,7 @@ use codex_protocol::mcp_protocol::ExecCommandApprovalResponse;
use codex_protocol::mcp_protocol::InputItem as WireInputItem;
use codex_protocol::mcp_protocol::InterruptConversationParams;
use codex_protocol::mcp_protocol::InterruptConversationResponse;
use codex_protocol::mcp_protocol::LoginChatGptCompleteNotification;
use codex_protocol::mcp_protocol::LoginChatGptResponse;
use codex_protocol::mcp_protocol::GitDiffToRemoteParams;
// Unused login-related and diff param imports removed
use codex_protocol::mcp_protocol::GitDiffToRemoteResponse;
use codex_protocol::mcp_protocol::NewConversationParams;
use codex_protocol::mcp_protocol::NewConversationResponse;
@@ -61,31 +53,17 @@ use codex_protocol::mcp_protocol::SendUserMessageParams;
use codex_protocol::mcp_protocol::SendUserMessageResponse;
use codex_protocol::mcp_protocol::SendUserTurnParams;
use codex_protocol::mcp_protocol::SendUserTurnResponse;
use codex_protocol::mcp_protocol::ServerNotification;
// Duration before a ChatGPT login attempt is abandoned.
const LOGIN_CHATGPT_TIMEOUT: Duration = Duration::from_secs(10 * 60);
struct ActiveLogin {
shutdown_handle: ShutdownHandle,
login_id: Uuid,
}
impl ActiveLogin {
fn drop(&self) {
self.shutdown_handle.shutdown();
}
}
// Removed deprecated ChatGPT login support scaffolding
/// Handles JSON-RPC messages for Codex conversations.
pub(crate) struct CodexMessageProcessor {
auth_manager: Arc<AuthManager>,
_auth_manager: Arc<AuthManager>,
conversation_manager: Arc<ConversationManager>,
outgoing: Arc<OutgoingMessageSender>,
codex_linux_sandbox_exe: Option<PathBuf>,
config: Arc<Config>,
_config: Arc<Config>,
conversation_listeners: HashMap<Uuid, oneshot::Sender<()>>,
active_login: Arc<Mutex<Option<ActiveLogin>>>,
// Queue of pending interrupt requests per conversation. We reply when TurnAborted arrives.
pending_interrupts: Arc<Mutex<HashMap<Uuid, Vec<RequestId>>>>,
}
@@ -99,13 +77,12 @@ impl CodexMessageProcessor {
config: Arc<Config>,
) -> Self {
Self {
auth_manager,
_auth_manager: auth_manager,
conversation_manager,
outgoing,
codex_linux_sandbox_exe,
config,
_config: config,
conversation_listeners: HashMap::new(),
active_login: Arc::new(Mutex::new(None)),
pending_interrupts: Arc::new(Mutex::new(HashMap::new())),
}
}
@@ -457,7 +434,7 @@ async fn apply_bespoke_event_handling(
conversation_id: ConversationId,
conversation: Arc<CodexConversation>,
outgoing: Arc<OutgoingMessageSender>,
pending_interrupts: Arc<Mutex<HashMap<Uuid, Vec<RequestId>>>>,
_pending_interrupts: Arc<Mutex<HashMap<Uuid, Vec<RequestId>>>>,
) {
let Event { id: event_id, msg } = event;
match msg {
@@ -122,6 +122,7 @@ impl OutgoingMessageSender {
.await;
}
#[allow(dead_code)]
pub(crate) async fn send_server_notification(&self, notification: ServerNotification) {
let method = format!("codex/event/{}", notification);
let params = match serde_json::to_value(&notification) {
+6 -8
View File
@@ -22,8 +22,6 @@ use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
use crossterm::event::KeyEventKind;
use crossterm::execute;
use crossterm::terminal::EnterAlternateScreen;
use crossterm::terminal::LeaveAlternateScreen;
use crossterm::terminal::supports_keyboard_enhancement;
use std::path::PathBuf;
use ratatui::prelude::Rect;
@@ -72,9 +70,9 @@ pub(crate) struct App<'a> {
pending_redraw: Arc<AtomicBool>,
// Transcript overlay state
transcript_overlay: Option<TranscriptApp>,
deferred_history_lines: Vec<Line<'static>>,
transcript_saved_viewport: Option<Rect>,
_transcript_overlay: Option<TranscriptApp>,
_deferred_history_lines: Vec<Line<'static>>,
_transcript_saved_viewport: Option<Rect>,
enhanced_keys_supported: bool,
@@ -216,9 +214,9 @@ impl App<'_> {
config,
file_search,
pending_redraw,
transcript_overlay: None,
deferred_history_lines: Vec::new(),
transcript_saved_viewport: None,
_transcript_overlay: None,
_deferred_history_lines: Vec::new(),
_transcript_saved_viewport: None,
enhanced_keys_supported,
_debug: debug,
commit_anim_running: Arc::new(AtomicBool::new(false)),
+1
View File
@@ -81,6 +81,7 @@ pub(crate) enum AppEvent {
},
/// Result of computing a `/diff` command.
#[allow(dead_code)]
DiffResult(String),
InsertHistory(Vec<Line<'static>>),
@@ -1,3 +1,4 @@
#![allow(dead_code)]
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
use crossterm::event::KeyModifiers;
@@ -5,7 +5,6 @@ use crossterm::event::KeyModifiers;
use ratatui::buffer::Buffer;
use ratatui::layout::Alignment;
use ratatui::layout::Rect;
use ratatui::style::Color;
use ratatui::style::Modifier;
use ratatui::style::Style;
use ratatui::text::Line;
@@ -5,7 +5,6 @@ use crossterm::event::KeyModifiers;
use ratatui::buffer::Buffer;
use ratatui::layout::Alignment;
use ratatui::layout::Rect;
use ratatui::style::Color;
use ratatui::style::Modifier;
use ratatui::style::Style;
use ratatui::text::Line;
+10 -13
View File
@@ -35,7 +35,6 @@ use codex_core::protocol::ExecApprovalRequestEvent;
use codex_core::protocol::ExecCommandBeginEvent;
use codex_core::protocol::ExecCommandEndEvent;
use codex_core::protocol::InputItem;
use codex_protocol::protocol::McpListToolsResponseEvent;
use codex_core::protocol::McpToolCallBeginEvent;
use codex_core::protocol::McpToolCallEndEvent;
use codex_core::protocol::Op;
@@ -59,8 +58,6 @@ use std::cell::RefCell;
use tokio::sync::mpsc::UnboundedSender;
use tokio::sync::mpsc::unbounded_channel;
use tracing::info;
use crate::slash_command::SlashCommand;
use crate::get_git_diff::get_git_diff;
// use image::GenericImageView;
use crate::app_event::AppEvent;
@@ -69,8 +66,6 @@ use crate::bottom_pane::BottomPane;
use crate::bottom_pane::BottomPaneParams;
use crate::bottom_pane::CancellationEvent;
use crate::bottom_pane::InputResult;
use crate::bottom_pane::list_selection_view::SelectionAction;
use crate::bottom_pane::list_selection_view::SelectionItem;
use crate::history_cell;
use crate::history_cell::CommandOutput;
use crate::history_cell::ExecCell;
@@ -678,7 +673,7 @@ impl ChatWidget<'_> {
// Try to replace the placeholder running cell in place to preserve order.
let mut replaced = false;
if let Some(mut idx) = history_index {
if let Some(idx) = history_index {
if idx < self.history_cells.len() {
// Sanity check: ensure it's a running exec cell for the same command
let is_match = self.history_cells[idx]
@@ -2041,7 +2036,7 @@ impl ChatWidget<'_> {
if !original_text.is_empty() {
// Check if this is the first user prompt to trigger fade-out animation
let has_existing_user_prompts = self.history_cells.iter().any(|cell| {
let _has_existing_user_prompts = self.history_cells.iter().any(|cell| {
// Check if it's a user prompt by looking at display lines
// This is a bit indirect but works with the trait-based system
let lines = cell.display_lines();
@@ -4384,6 +4379,7 @@ impl ChatWidget<'_> {
self.add_to_history(history_cell::PlainHistoryCell { lines, kind: history_cell::HistoryCellType::BackgroundEvent });
}
#[allow(dead_code)]
fn switch_to_internal_browser(&mut self) {
// Switch to internal browser mode
let latest_screenshot = self.latest_browser_screenshot.clone();
@@ -5567,14 +5563,15 @@ impl WidgetRef for &ChatWidget<'_> {
// Record current viewport height for the next frame
self.last_history_viewport_height.set(content_area.height);
// Clear the content area to prevent stale characters from previous frames
// This fixes the "stuck characters" issue when scrolling through chat history
// We fill with spaces while preserving the theme background color
// Clear the entire history region (including left/right padding), not just
// the inner content area. This avoids occasional artifacts at the margins
// and ensures background is fully painted even when widths shrink.
// Fill with spaces while preserving the theme background color.
let clear_style = Style::default()
.bg(crate::colors::background())
.fg(crate::colors::text());
for y in content_area.y..(content_area.y + content_area.height) {
for x in content_area.x..(content_area.x + content_area.width) {
for y in history_area.y..history_area.y.saturating_add(history_area.height) {
for x in history_area.x..history_area.x.saturating_add(history_area.width) {
buf[(x, y)].set_char(' ').set_style(clear_style);
}
}
@@ -5816,7 +5813,7 @@ impl WidgetRef for &ChatWidget<'_> {
}
// Match the horizontal padding used by status bar and input
let padding = 1u16;
let mut area = Rect {
let area = Rect {
x: history_area.x + padding,
y: history_area.y,
width: history_area.width.saturating_sub(padding * 2),
+1
View File
@@ -52,6 +52,7 @@ pub(crate) fn border_dim() -> Color {
Color::Rgb(r, g, bl)
}
#[allow(dead_code)]
pub(crate) fn border_focused() -> Color {
current_theme().border_focused
}
+4 -4
View File
@@ -46,7 +46,7 @@ impl Default for HeightManagerConfig {
#[derive(Default)]
struct DevCounters {
frames: u64,
hysteresis_applied: u64,
_hysteresis_applied: u64,
hud_quantized: u64,
}
@@ -136,8 +136,8 @@ impl HeightManager {
let desired = bottom_desired_height.max(5).min(bottom_cap);
// Bottom pane policy: Grow immediately, confirm small decreases over a few frames
let mut bottom_h = match (self.last_bottom, self.bypass_once) {
(Some(prev), true) => desired,
let bottom_h = match (self.last_bottom, self.bypass_once) {
(Some(_), true) => desired,
(Some(prev), false) => {
if desired > prev {
// grow immediately, reset counter
@@ -172,7 +172,7 @@ impl HeightManager {
if self.bypass_once { self.bypass_once = false; }
// Determine HUD height if present.
let mut hud_h: u16 = 0;
let mut hud_h: u16;
if hud_present {
// This mirrors the existing preview logic while centralizing its decision.
// Compute HUD target height using 16:9 aspect on inner width.
+5 -3
View File
@@ -11,7 +11,6 @@ use codex_core::parse_command::ParsedCommand;
use codex_core::plan_tool::PlanItemArg;
use codex_core::plan_tool::StepStatus;
use codex_core::plan_tool::UpdatePlanArgs;
use codex_core::project_doc::discover_project_doc_paths;
use codex_core::protocol::FileChange;
use crate::diff_render::create_diff_summary;
use codex_core::protocol::McpInvocation;
@@ -782,6 +781,7 @@ impl HistoryCell for ImageOutputCell {
// ==================== ToolCallCell ====================
pub(crate) enum ToolState {
#[allow(dead_code)]
Running,
Success,
Failed,
@@ -1091,7 +1091,7 @@ impl HistoryCell for StreamingContentCell {
const PREVIEW_HEAD_LINES: usize = 2;
const PREVIEW_TAIL_LINES: usize = 5;
fn build_preview_lines(text: &str, include_left_pipe: bool) -> Vec<Line<'static>> {
fn build_preview_lines(text: &str, _include_left_pipe: bool) -> Vec<Line<'static>> {
let processed = format_json_compact(text).unwrap_or_else(|| text.to_string());
let non_empty: Vec<&str> = processed
.lines()
@@ -1783,6 +1783,7 @@ fn new_exec_command_generic(
lines
}
#[allow(dead_code)]
pub(crate) fn new_active_mcp_tool_call(invocation: McpInvocation) -> ToolCallCell {
let title_line = Line::styled("Working", Style::default().fg(crate::colors::primary()));
let lines: Vec<Line> = vec![
@@ -1793,6 +1794,7 @@ pub(crate) fn new_active_mcp_tool_call(invocation: McpInvocation) -> ToolCallCel
ToolCallCell { lines, state: ToolState::Running }
}
#[allow(dead_code)]
pub(crate) fn new_active_custom_tool_call(tool_name: String, args: Option<String>) -> ToolCallCell {
let title_line = Line::styled("Working", Style::default().fg(crate::colors::primary()));
let invocation_str = if let Some(args) = args {
@@ -2207,7 +2209,7 @@ fn try_new_completed_mcp_tool_call_with_image_output(
}
pub(crate) fn new_completed_mcp_tool_call(
num_cols: usize,
_num_cols: usize,
invocation: McpInvocation,
duration: Duration,
success: bool,
+1
View File
@@ -279,6 +279,7 @@ where
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
pub struct SetUnderlineColor(pub CColor);
impl Command for SetUnderlineColor {
-1
View File
@@ -18,7 +18,6 @@ use codex_ollama::DEFAULT_OSS_MODEL;
use codex_protocol::config_types::SandboxMode;
use std::fs::OpenOptions;
use std::path::PathBuf;
use tracing::error;
use tracing_appender::non_blocking;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::prelude::*;
+2
View File
@@ -2,6 +2,7 @@ use unicode_segmentation::UnicodeSegmentation;
/// Truncate a tool result to fit within the given height and width. If the text is valid JSON, we format it in a compact way before truncating.
/// This is a best-effort approach that may not work perfectly for text where 1 grapheme is rendered as multiple terminal cells.
#[allow(dead_code)]
pub(crate) fn format_and_truncate_tool_result(
text: &str,
max_lines: usize,
@@ -74,6 +75,7 @@ pub(crate) fn format_json_compact(text: &str) -> Option<String> {
}
/// Truncate `text` to `max_graphemes` graphemes. Using graphemes to avoid accidentally truncating in the middle of a multi-codepoint character.
#[allow(dead_code)]
pub(crate) fn truncate_text(text: &str, max_graphemes: usize) -> String {
let mut graphemes = text.grapheme_indices(true);
+2 -1
View File
@@ -1,3 +1,4 @@
#![allow(dead_code)]
use std::io::Result;
use crate::insert_history;
@@ -50,7 +51,7 @@ impl TranscriptApp {
self.transcript_lines.extend(lines);
}
fn handle_key_event(&mut self, tui: &mut tui::Tui, key_event: KeyEvent) {
fn handle_key_event(&mut self, _tui: &mut tui::Tui, key_event: KeyEvent) {
match key_event {
KeyEvent {
code: KeyCode::Char('q'),