Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ web-sys = { version = "0.3.77", features = [
'HtmlCanvasElement',
'Location',
] }
ratatui = { version = "0.29", default-features = false, features = ["all-widgets"] }
ratatui = { git = "https://github.com/jetpham/ratatui.git", branch = "hyperlink", version = "0.29", default-features = false, features = ["all-widgets"] }
console_error_panic_hook = "0.1.7"
thiserror = "2.0.12"
log = "0.4.27"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use console::log_1 for logging in the browser btw :) no need for log

24 changes: 21 additions & 3 deletions examples/website/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/website/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ fn render_intro(f: &mut Frame<'_>, state: &mut State) {
Line::from("Stomping through the web").italic(),
]);
f.render_widget(main_text.light_green().centered(), area);
let link = Hyperlink::new("https://github.com/orhun/ratzilla".red());
let link = Hyperlink::new("ratzilla".red(), "https://github.com/orhun/ratzilla");
f.render_widget(link, area.offset(Offset { x: 0, y: 4 }));
f.render_effect(&mut state.intro_effect, area, Duration::from_millis(40));
}
Expand Down
47 changes: 17 additions & 30 deletions src/backend/dom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use web_sys::{
window, Document, Element, Window,
};

use crate::{backend::utils::*, error::Error, widgets::hyperlink::HYPERLINK_MODIFIER};
use crate::{backend::utils::*, error::Error};

/// Options for the [`DomBackend`].
#[derive(Debug, Default)]
Expand All @@ -30,7 +30,7 @@ impl DomBackendOptions {
///
/// - If the grid ID is not set, it returns `"grid"`.
/// - If the grid ID is set, it returns the grid ID suffixed with
/// `"_ratzilla_grid"`.
/// `"_ratzilla_grid"`.
pub fn grid_id(&self) -> String {
match &self.grid_id {
Some(id) => format!("{id}_ratzilla_grid"),
Expand Down Expand Up @@ -131,30 +131,10 @@ impl DomBackend {
fn prerender(&mut self) -> Result<(), Error> {
for line in self.buffer.iter() {
let mut line_cells: Vec<Element> = Vec::new();
let mut hyperlink: Vec<Cell> = Vec::new();
for (i, cell) in line.iter().enumerate() {
if cell.modifier.contains(HYPERLINK_MODIFIER) {
hyperlink.push(cell.clone());
// If the next cell is not part of the hyperlink, close it
if !line
.get(i + 1)
.map(|c| c.modifier.contains(HYPERLINK_MODIFIER))
.unwrap_or(false)
{
let anchor = create_anchor(&self.document, &hyperlink)?;
for link_cell in &hyperlink {
let span = create_span(&self.document, link_cell)?;
self.cells.push(span.clone());
anchor.append_child(&span)?;
}
line_cells.push(anchor);
hyperlink.clear();
}
} else {
let span = create_span(&self.document, cell)?;
self.cells.push(span.clone());
line_cells.push(span);
}
for cell in line.iter() {
let span = create_span(&self.document, cell)?;
self.cells.push(span.clone());
line_cells.push(span);
}

// Create a <pre> element for the line
Expand All @@ -176,13 +156,20 @@ impl DomBackend {
fn update_grid(&mut self) -> Result<(), Error> {
for (y, line) in self.buffer.iter().enumerate() {
for (x, cell) in line.iter().enumerate() {
if cell.modifier.contains(HYPERLINK_MODIFIER) {
continue;
}
if cell != &self.prev_buffer[y][x] {
let elem = self.cells[y * self.buffer[0].len() + x].clone();
elem.set_inner_html(cell.symbol());
elem.set_attribute("style", &get_cell_style_as_css(cell))?;
if let Some(anchor) = elem.first_element_child() {
if let Some(url) = cell.hyperlink() {
anchor.set_attribute("href", url)?;
anchor.set_inner_html(cell.symbol());
} else {
anchor.remove();
elem.set_inner_html(cell.symbol());
}
} else {
elem.set_inner_html(cell.symbol());
}
}
}
}
Expand Down
22 changes: 10 additions & 12 deletions src/backend/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,20 @@ use crate::{
/// Creates a new `<span>` element with the given cell.
pub(crate) fn create_span(document: &Document, cell: &Cell) -> Result<Element, Error> {
let span = document.create_element("span")?;
span.set_inner_html(cell.symbol());

let style = get_cell_style_as_css(cell);
span.set_attribute("style", &style)?;
Ok(span)
}

/// Creates a new `<a>` element with the given cells.
pub(crate) fn create_anchor(document: &Document, cells: &[Cell]) -> Result<Element, Error> {
let anchor = document.create_element("a")?;
anchor.set_attribute(
"href",
&cells.iter().map(|c| c.symbol()).collect::<String>(),
)?;
anchor.set_attribute("style", &get_cell_style_as_css(&cells[0]))?;
Ok(anchor)
if let Some(url) = cell.hyperlink() {
let anchor = document.create_element("a")?;
anchor.set_attribute("href", url)?;
anchor.set_attribute("style", "text-decoration: none; color: inherit")?;
anchor.set_inner_html(cell.symbol());
span.append_child(&anchor)?;
} else {
span.set_inner_html(cell.symbol());
}
Ok(span)
}

/// Converts a cell to a CSS style.
Expand Down
21 changes: 10 additions & 11 deletions src/widgets/hyperlink.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
use ratatui::{buffer::Buffer, layout::Rect, style::Modifier, text::Span, widgets::Widget};

/// Hyperlink modifier.
///
/// When added as a modifier to a style, the styled element is marked as
/// hyperlink.
pub(crate) const HYPERLINK_MODIFIER: Modifier = Modifier::SLOW_BLINK;
use log::info;
use ratatui::{buffer::Buffer, layout::Rect, style::Style, text::Span, widgets::Widget};

/// A widget that can be used to render hyperlinks.
///
Expand All @@ -16,20 +11,24 @@ pub(crate) const HYPERLINK_MODIFIER: Modifier = Modifier::SLOW_BLINK;
/// // Then you can render it as usual:
/// // frame.render_widget(link, frame.area());
/// ```
#[derive(Debug)]
pub struct Hyperlink<'a> {
/// Line.
line: Span<'a>,
}

impl<'a> Hyperlink<'a> {
/// Constructs a new [`Hyperlink`] widget.
pub fn new<T>(url: T) -> Self
pub fn new<T, U>(content: T, url: U) -> Self
where
T: Into<Span<'a>>,
U: Into<&'static str>,
{
Self {
line: url.into().style(HYPERLINK_MODIFIER),
}
let line = content
.into()
.patch_style(Style::new().hyperlink(url.into()));
// info!("span: {:#?}", line);
Self { line }
}
}

Expand Down