Files
codex/codex-rs/tui/src/external_agent_config_migration/render.rs
T
stefanstokic-oai 743a4147d9 [codex] restore source-specific import copy (#27703)
## Summary

- restore source-specific wording across the `/import` picker, lifecycle
messages, diagnostics, and help text
- update the matching Unix and Windows snapshots
- leave import behavior unchanged

## Why

The import path currently supports one source, so the UI should identify
that source directly instead of presenting the flow as
provider-agnostic.

## Validation

- `just test -p codex-tui external_agent_config_migration` (12 passed)
- `just fix -p codex-tui`
- `just fmt`
2026-06-12 10:19:17 -04:00

207 lines
7.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use super::*;
use crate::key_hint;
use crate::line_truncation::truncate_line_with_ellipsis_if_overflow;
use crate::render::Insets;
use crate::render::RectExt as _;
use crate::selection_list::selection_option_row_with_dim;
use ratatui::buffer::Buffer;
use ratatui::layout::Constraint;
use ratatui::layout::Layout;
use ratatui::layout::Rect;
use ratatui::widgets::Clear;
use ratatui::widgets::Paragraph;
use ratatui::widgets::Widget;
use ratatui::widgets::WidgetRef;
use ratatui::widgets::Wrap;
impl ExternalAgentConfigMigrationScreen {
fn render_items(&self, area: Rect, buf: &mut Buffer) {
if area.height == 0 || area.width == 0 {
return;
}
let rows = self.build_render_lines();
let visible_rows = area.height as usize;
let mut start_idx = self.scroll_top.min(rows.len().saturating_sub(1));
if let Some(selected_item_idx) = self.selected_item_idx {
let selected_render_idx = self.selected_render_line_index(selected_item_idx);
if selected_render_idx < start_idx {
start_idx = selected_render_idx;
} else if visible_rows > 0 {
let bottom = start_idx + visible_rows - 1;
if selected_render_idx > bottom {
start_idx = selected_render_idx + 1 - visible_rows;
}
}
}
let mut y = area.y;
for entry in rows.iter().skip(start_idx).take(visible_rows) {
if y >= area.y + area.height {
break;
}
let selected =
self.focus == FocusArea::Items && self.selected_item_idx == entry.item_idx;
let mut line = entry.line.clone();
if selected {
if let Some(cursor) = line.spans.first_mut() {
cursor.content = "› ".into();
}
line.spans.iter_mut().for_each(|span| {
span.style = span.style.cyan().bold();
});
} else if entry.kind != RenderLineKind::Item && !line.spans.is_empty() {
line.spans.iter_mut().for_each(|span| {
span.style = span.style.dim();
});
}
let line = truncate_line_with_ellipsis_if_overflow(line, area.width as usize);
line.render(
Rect {
x: area.x,
y,
width: area.width,
height: 1,
},
buf,
);
y = y.saturating_add(1);
}
}
}
impl WidgetRef for &ExternalAgentConfigMigrationScreen {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
Clear.render(area, buf);
let inner_area = area.inset(Insets::vh(/*v*/ 1, /*h*/ 2));
let error_height = u16::from(self.error.is_some());
let intro_lines = match self.view {
MigrationView::Summary => vec![
Line::from("Bring over your setup, current project, and recent chats."),
Line::from("Codex may add files to your current project folder."),
Line::from("Your existing Claude Code setup will not be changed."),
Line::from("Standard Claude Chat data cannot be imported."),
],
MigrationView::Customize => vec![
Line::from("Choose the Claude Code items to import."),
Line::from("Codex may add files to your current project folder."),
Line::from("Your existing Claude Code setup will not be changed."),
],
};
let intro_height = intro_lines.len() as u16;
let actions = self.available_actions();
let actions_height = actions.len() as u16 + 1;
let fixed_height = 1u16 + intro_height + error_height + 1u16 + actions_height + 1u16;
let list_height =
self.render_line_count()
.max(1)
.min(inner_area.height.saturating_sub(fixed_height) as usize) as u16;
let [
header_area,
intro_area,
error_area,
list_area,
list_gap_area,
actions_area,
footer_area,
_spacer_area,
] = Layout::vertical([
Constraint::Length(1),
Constraint::Length(intro_height),
Constraint::Length(error_height),
Constraint::Length(list_height),
Constraint::Length(1),
Constraint::Length(actions_height),
Constraint::Length(1),
Constraint::Fill(1),
])
.areas(inner_area);
let title = match self.view {
MigrationView::Summary => "Import from Claude Code",
MigrationView::Customize => "Choose what to import",
};
let heading = Line::from(vec!["> ".into(), title.bold()]);
heading.render(header_area, buf);
Paragraph::new(intro_lines)
.wrap(Wrap { trim: false })
.render(intro_area, buf);
if let Some(error) = &self.error {
Paragraph::new(error.clone().red().to_string())
.wrap(Wrap { trim: false })
.render(error_area, buf);
}
self.render_items(list_area, buf);
Clear.render(list_gap_area, buf);
let action_areas = Layout::vertical(std::iter::repeat_n(
Constraint::Length(1),
actions.len() + 1,
))
.split(actions_area);
let item_label = if self.items.len() == 1 {
"item"
} else {
"items"
};
let actions_intro = format!(
"Selected {} of {} {item_label}.",
self.selected_count(),
self.items.len()
);
Paragraph::new(actions_intro)
.wrap(Wrap { trim: false })
.render(action_areas[0], buf);
for (idx, action) in actions.iter().enumerate() {
selection_option_row_with_dim(
idx,
action.label().to_string(),
self.focus == FocusArea::Actions && self.highlighted_action == *action,
/*dim*/ self.focus != FocusArea::Actions,
)
.render(action_areas[idx + 1], buf);
}
let footer = match self.view {
MigrationView::Summary => Line::from(vec![
"Use ".dim(),
key_hint::plain(KeyCode::Up).into(),
"/".dim(),
key_hint::plain(KeyCode::Down).into(),
" to move, ".dim(),
key_hint::plain(KeyCode::Enter).into(),
" to select, ".dim(),
"c".cyan(),
" to customize".dim(),
]),
MigrationView::Customize if self.focus == FocusArea::Actions => Line::from(vec![
"Press ".dim(),
key_hint::plain(KeyCode::Enter).into(),
" to continue, ".dim(),
key_hint::plain(KeyCode::Up).into(),
"/".dim(),
key_hint::plain(KeyCode::Down).into(),
" to move, ".dim(),
"b".cyan(),
" to go back".dim(),
]),
MigrationView::Customize => Line::from(vec![
"Use ".dim(),
key_hint::plain(KeyCode::Up).into(),
"/".dim(),
key_hint::plain(KeyCode::Down).into(),
" to move, ".dim(),
key_hint::plain(KeyCode::Char(' ')).into(),
" to toggle, ".dim(),
"b".cyan(),
" to go back".dim(),
]),
};
footer.render(footer_area, buf);
}
}