Codex Handbook
tui/src/history_cell/notices.rs 203 lines
//! Informational, warning, update, and policy notice history cells.use super::*;#[cfg_attr(debug_assertions, allow(dead_code))]#[derive(Debug)]pub(crate) struct UpdateAvailableHistoryCell {    latest_version: String,    update_action: Option<UpdateAction>,}#[cfg_attr(debug_assertions, allow(dead_code))]impl UpdateAvailableHistoryCell {    pub(crate) fn new(latest_version: String, update_action: Option<UpdateAction>) -> Self {        Self {            latest_version,            update_action,        }    }}impl HistoryCell for UpdateAvailableHistoryCell {    fn display_lines(&self, width: u16) -> Vec<Line<'static>> {        use ratatui_macros::line;        use ratatui_macros::text;        let update_instruction = if let Some(update_action) = self.update_action {            line!["Run ", update_action.command_str().cyan(), " to update."]        } else {            line![                "See ",                "https://github.com/openai/codex".cyan().underlined(),                " for installation options."            ]        };        let content = text![            line![                padded_emoji("✨").bold().cyan(),                "Update available!".bold().cyan(),                " ",                format!("{CODEX_CLI_VERSION} -> {}", self.latest_version).bold(),            ],            update_instruction,            "",            "See full release notes:",            "https://github.com/openai/codex/releases/latest"                .cyan()                .underlined(),        ];        let inner_width = content            .width()            .min(usize::from(width.saturating_sub(4)))            .max(1);        let lines = adaptive_wrap_lines(content.lines, RtOptions::new(inner_width));        with_border_with_inner_width(lines, inner_width)    }    fn raw_lines(&self) -> Vec<Line<'static>> {        let update_instruction = if let Some(update_action) = self.update_action {            format!("Run {} to update.", update_action.command_str())        } else {            "See https://github.com/openai/codex for installation options.".to_string()        };        vec![            Line::from("Update available!"),            Line::from(format!("{CODEX_CLI_VERSION} -> {}", self.latest_version)),            Line::from(update_instruction),            Line::from(""),            Line::from("See full release notes:"),            Line::from("https://github.com/openai/codex/releases/latest"),        ]    }    fn display_hyperlink_lines(&self, width: u16) -> Vec<HyperlinkLine> {        crate::terminal_hyperlinks::annotate_web_urls(self.display_lines(width))    }    fn transcript_hyperlink_lines(&self, width: u16) -> Vec<HyperlinkLine> {        self.display_hyperlink_lines(width)    }}#[allow(clippy::disallowed_methods)]pub(crate) fn new_warning_event(message: String) -> PrefixedWrappedHistoryCell {    PrefixedWrappedHistoryCell::new(message.yellow(), "⚠ ".yellow(), "  ")}const TRUSTED_ACCESS_FOR_CYBER_URL: &str = "https://chatgpt.com/cyber";#[derive(Debug)]pub(crate) struct CyberPolicyNoticeCell;pub(crate) fn new_cyber_policy_error_event() -> CyberPolicyNoticeCell {    CyberPolicyNoticeCell}impl HistoryCell for CyberPolicyNoticeCell {    fn display_lines(&self, width: u16) -> Vec<Line<'static>> {        let mut lines: Vec<Line<'static>> = Vec::new();        lines.push(            vec![                "ⓘ ".cyan(),                "This chat was flagged for possible cybersecurity risk".bold(),            ]            .into(),        );        let wrap_width = width.saturating_sub(2).max(1) as usize;        let body = Line::from(vec![            "  If this seems wrong, try rephrasing your request. To get authorized for security work, join the "                .dim(),            "Trusted Access for Cyber".cyan().underlined(),            " program.".dim(),        ]);        let wrapped = adaptive_wrap_line(            &body,            RtOptions::new(wrap_width).subsequent_indent("  ".into()),        );        push_owned_lines(&wrapped, &mut lines);        lines.push(            vec![                "  ".into(),                TRUSTED_ACCESS_FOR_CYBER_URL.cyan().underlined(),            ]            .into(),        );        lines    }    fn raw_lines(&self) -> Vec<Line<'static>> {        vec![            Line::from("This chat was flagged for possible cybersecurity risk"),            Line::from(                "If this seems wrong, try rephrasing your request. To get authorized for security work, join the Trusted Access for Cyber program.",            ),            Line::from(TRUSTED_ACCESS_FOR_CYBER_URL),        ]    }    fn display_hyperlink_lines(&self, width: u16) -> Vec<HyperlinkLine> {        crate::terminal_hyperlinks::annotate_web_urls(self.display_lines(width))    }    fn transcript_hyperlink_lines(&self, width: u16) -> Vec<HyperlinkLine> {        self.display_hyperlink_lines(width)    }}#[derive(Debug)]pub(crate) struct DeprecationNoticeCell {    summary: String,    details: Option<String>,}pub(crate) fn new_deprecation_notice(    summary: String,    details: Option<String>,) -> DeprecationNoticeCell {    DeprecationNoticeCell { summary, details }}impl HistoryCell for DeprecationNoticeCell {    fn display_lines(&self, width: u16) -> Vec<Line<'static>> {        let mut lines: Vec<Line<'static>> = Vec::new();        lines.push(vec!["⚠ ".red().bold(), self.summary.clone().red()].into());        let wrap_width = width.saturating_sub(4).max(1) as usize;        if let Some(details) = &self.details {            let detail_line = Line::from(details.clone().dim());            let wrapped = adaptive_wrap_line(&detail_line, RtOptions::new(wrap_width));            push_owned_lines(&wrapped, &mut lines);        }        lines    }    fn raw_lines(&self) -> Vec<Line<'static>> {        let mut lines = vec![Line::from(self.summary.clone())];        if let Some(details) = &self.details {            lines.extend(raw_lines_from_source(details));        }        lines    }}pub(crate) fn new_info_event(message: String, hint: Option<String>) -> PlainHistoryCell {    let mut line = vec!["• ".dim(), message.into()];    if let Some(hint) = hint {        line.push(" ".into());        line.push(hint.dark_gray());    }    let lines: Vec<Line<'static>> = vec![line.into()];    PlainHistoryCell { lines }}pub(crate) fn new_error_event(message: String) -> PlainHistoryCell {    // Use a hair space (U+200A) to create a subtle, near-invisible separation    // before the text. VS16 is intentionally omitted to keep spacing tighter    // in terminals like Ghostty.    let lines: Vec<Line<'static>> = vec![vec![format!("■ {message}").red()].into()];    PlainHistoryCell { lines }}