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

Support styling as a hyperlink #1028

Open
mo8it opened this issue Apr 12, 2024 · 7 comments
Open

Support styling as a hyperlink #1028

mo8it opened this issue Apr 12, 2024 · 7 comments
Labels
Status: Design Needed An issue that isn't yet designed or specified well enough to implement Status: Pending Waiting on clarification / more information from submitter Type: Enhancement New feature or request

Comments

@mo8it
Copy link
Contributor

mo8it commented Apr 12, 2024

See crossterm-rs/crossterm#602 and https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda

@mo8it mo8it added the Type: Enhancement New feature or request label Apr 12, 2024
@joshka
Copy link
Member

joshka commented Apr 12, 2024

I want this too. I suspect OSC 8 will just work if #902 is fixed.
A super high level on that issue is that the calculation of how many cells to skip needs to take into account that ANSI escape codes might mean that we write many symbols to the same cell.

A full solution to this would be to add a hyperlink widget that has properties for the text and hyperlink so app code can just do:

Hyperlink::new("Google", "https://google.com).render(area, buf);

Alternatively, I think as phrased you might be looking to add the hyperlink as another part of the Style struct, which would allow it to be added to any span/line/text without having to break the flow. Thoughts?

@mo8it
Copy link
Contributor Author

mo8it commented Apr 14, 2024

Thanks for your quick reply 🤗

A widget would be too limiting. I think that this should be part of a Paragraph since such a hyperlink can be part of a longer text and might be broken/wrapped into multiple lines (so not only in one Line).

@joshka
Copy link
Member

joshka commented Apr 25, 2024

To be clear, a Hyperlink Widget as proposed above would accept anything that supports Into<Text>. I'd like eventually to move the wrapping into Text / Line / Span instead of wrapping being implemented in Paragraph only.

@joshka
Copy link
Member

joshka commented Apr 25, 2024

Demo

/// A hyperlink widget that renders a hyperlink in the terminal using [OSC 8].
///
/// [OSC 8]: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
struct Hyperlink<'content> {
    text: Text<'content>,
    url: String,
}

impl<'content> Hyperlink<'content> {
    fn new(text: impl Into<Text<'content>>, url: impl Into<String>) -> Self {
        Self {
            text: text.into(),
            url: url.into(),
        }
    }
}

impl WidgetRef for Hyperlink<'_> {
    fn render_ref(&self, area: Rect, buffer: &mut Buffer) {
        self.text.render_ref(area, buffer);

        // this is a hacky workaround for https://github.com/ratatui-org/ratatui/issues/902, a bug
        // in the terminal code that incorrectly calculates the width of ANSI escape sequences. It
        // works by rendering the hyperlink as a series of 2-character chunks, which is the
        // calculated width of the hyperlink text.
        for (i, two_chars) in self
            .text
            .to_string()
            .chars()
            .chunks(2)
            .into_iter()
            .enumerate()
        {
            let text = two_chars.collect::<String>();
            let hyperlink = format!("\x1B]8;;{}\x07{}\x1B]8;;\x07", self.url, text);
            buffer
                .get_mut(area.x + i as u16 * 2, area.y)
                .set_symbol(hyperlink.as_str());
        }
    }
}

joshka added a commit that referenced this issue Apr 25, 2024
@joshka joshka added Type: Enhancement Type: Enhancement New feature or request Status: Design Needed An issue that isn't yet designed or specified well enough to implement and removed Type: Enhancement New feature or request Type: zEnhancement labels Jun 19, 2024
@joshka
Copy link
Member

joshka commented Jun 19, 2024

A widget would be too limiting. I think that this should be part of a Paragraph since such a hyperlink can be part of a longer text and might be broken/wrapped into multiple lines (so not only in one Line).

Would you mind expanding a bit more on this point. I'm guessing with your rustlings work you have a bit more of an understanding of how Ratatui works now and might have thought more about how this idea might fit into things? Do you have any design ideas here?

@joshka joshka added the Status: Pending Waiting on clarification / more information from submitter label Jun 19, 2024
@mo8it
Copy link
Contributor Author

mo8it commented Jul 10, 2024

No, I didn't work long enough with Ratatui to be able to work on design ideas. I just wanted to mention that a link could be wrapped and therefore exist on two lines.

Example in a too small terminal pane:
image

@joshka
Copy link
Member

joshka commented Jul 10, 2024

If hyperlink was a part of Style, that would be represented as:

Line {
  spans: [
    Span::raw("A solution file can be found at "),
    Span::styled("solutions/quizzes/quiz2.rs", Style::new().hyperlink("solutions/quizzes/quiz2.rs")),
  ],
  ..Line::default()
}

(Lines are wrapped when put in a paragraph, OSC8 continues to the next line automatically AFAIK)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Design Needed An issue that isn't yet designed or specified well enough to implement Status: Pending Waiting on clarification / more information from submitter Type: Enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants