mirror of
https://github.com/openai/codex.git
synced 2026-11-01 19:33:35 +00:00
This introduces the use of the `tui-markdown` crate to parse an assistant message as Markdown and style it using ANSI for a better user experience. As shown in the screenshot below, it has support for syntax highlighting for _tagged_ fenced code blocks: <img width="907" alt="image" src="https://github.com/user-attachments/assets/900dc229-80bb-46e8-b1bb-efee4c70ba3c" /> That said, `tui-markdown` is not as configurable (or stylish!) as https://www.npmjs.com/package/marked-terminal, which is what we use in the TypeScript CLI. In particular: * The styles are hardcoded and `tui_markdown::from_str()` does not take any options whatsoever. It uses "bold white" for inline code style which does not stand out as much as the yellow used by `marked-terminal`: https://github.com/joshka/tui-markdown/blob/65402cbda70325f34e7ddf6fe1ec629bcd9459cf/tui-markdown/src/lib.rs#L464 I asked Codex to take a first pass at this and it came up with: https://github.com/joshka/tui-markdown/pull/80 * If a fenced code block is not tagged, then it does not get highlighted. I would rather add some logic here: https://github.com/joshka/tui-markdown/blob/65402cbda70325f34e7ddf6fe1ec629bcd9459cf/tui-markdown/src/lib.rs#L262 that uses something like https://pypi.org/project/guesslang/ to examine the value of `text` and try to use the appropriate syntax highlighter. * When we have a fenced code block, we do not want to show the opening and closing triple backticks in the output. To unblock ourselves, we might want to bundle our own fork of `tui-markdown` temporarily until we figure out what the shape of the API should be and then try to upstream it.
31 lines
1.3 KiB
Rust
31 lines
1.3 KiB
Rust
use ratatui::text::Line;
|
|
use ratatui::text::Span;
|
|
|
|
pub(crate) fn append_markdown(markdown_source: &str, lines: &mut Vec<Line<'static>>) {
|
|
let markdown = tui_markdown::from_str(markdown_source);
|
|
|
|
// `tui_markdown` returns a `ratatui::text::Text` where every `Line` borrows
|
|
// from the input `message` string. Since the `HistoryCell` stores its lines
|
|
// with a `'static` lifetime we must create an **owned** copy of each line
|
|
// so that it is no longer tied to `message`. We do this by cloning the
|
|
// content of every `Span` into an owned `String`.
|
|
|
|
for borrowed_line in markdown.lines {
|
|
let mut owned_spans = Vec::with_capacity(borrowed_line.spans.len());
|
|
for span in &borrowed_line.spans {
|
|
// Create a new owned String for the span's content to break the lifetime link.
|
|
let owned_span = Span::styled(span.content.to_string(), span.style);
|
|
owned_spans.push(owned_span);
|
|
}
|
|
|
|
let owned_line: Line<'static> = Line::from(owned_spans).style(borrowed_line.style);
|
|
// Preserve alignment if it was set on the source line.
|
|
let owned_line = match borrowed_line.alignment {
|
|
Some(alignment) => owned_line.alignment(alignment),
|
|
None => owned_line,
|
|
};
|
|
|
|
lines.push(owned_line);
|
|
}
|
|
}
|