Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to render markdown dynamically in real time #42

Closed
0x676e67 opened this issue Jun 10, 2023 · 6 comments · May be fixed by #47
Closed

How to render markdown dynamically in real time #42

0x676e67 opened this issue Jun 10, 2023 · 6 comments · May be fixed by #47

Comments

@0x676e67
Copy link

Code text similar to the stream returned by CHATGPT is rendered dynamically as a complement
image

The actual rendering looks like this:

image
@ifsheldon
Copy link

+1. I also wonder how this can be implemented. My current idea to work around this is to print raw strings and store these chunks, then once the completion is finished, clear the previous output and render the full content in md using termimad.

@Canop
Copy link
Owner

Canop commented Sep 15, 2023

Why wait for the end ? If you receive those chunks in an application, you can render them like in this example: https://github.com/Canop/termimad/tree/main/examples/render-input-markdown

@Canop
Copy link
Owner

Canop commented Sep 15, 2023

A word of caution: termimad isn't a universal renderer (there's not even any support for images). Its main use cases are for building terminal applications and it may not be suited to render what chatgpt outputs (no idea here).

@ifsheldon
Copy link

OK, I'm looking into it and will try to make a PR to add a simpler example.

@0x676e67
Copy link
Author

OK

@ifsheldon
Copy link

ifsheldon commented Sep 15, 2023

I think this is the minimum code to do this

use std::io::{stdout, Write};
use std::thread::sleep;
use termimad::crossterm::{cursor, ExecutableCommand};
use termimad::{FmtText, MadSkin};
use termimad::crossterm::terminal::Clear;
use termimad::crossterm::terminal::ClearType::FromCursorDown;

const MARKDOWN_TEXT: &str = r#"
# Hello

This is inline code `print("hello")`.

// wrap the below line in a code block
print("hello")

Here ends it.
"#;

fn main() {
    let skin = MadSkin::default();
    stdout().execute(cursor::Hide).unwrap();
    let mut string_buffer = String::new();
    for (i, chunk) in MARKDOWN_TEXT.chars().enumerate() {
        stdout()
            .execute(cursor::SavePosition).unwrap()
            .execute(Clear(FromCursorDown)).unwrap();
        string_buffer.push(chunk);
        let formatted_text = FmtText::from(&skin, &string_buffer, None); // can have Some(width) to enable hard wrapping
        print!("{}", formatted_text);
        stdout().flush().unwrap();
        let is_last_one = i == MARKDOWN_TEXT.len() - 1;
        if !is_last_one {
            stdout().execute(cursor::RestorePosition).unwrap();
        }
        sleep(std::time::Duration::from_millis(100));
    }
    stdout().execute(cursor::Show).unwrap();
    sleep(std::time::Duration::from_millis(500));
}

I got the idea from

let text = FmtText::from(&self.render_skin, &md, Some(self.render_area.width as usize - 1));

and
let text_view = TextView::from(&self.render_area, &text);

These two lines essentially do the same, which is rendering over and over again rather than incrementally rendering texts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants