Skip to content

Conversation

@VAWVAW
Copy link
Contributor

@VAWVAW VAWVAW commented Jan 31, 2026

This adds a a command :message to open an info screen that shows:

  • the message with image previews
  • the full user id of the sender
  • send timestamp including date
  • list of mentioned users
  • list of :openable links
  • list of reactions (including username of sender)
  • read receipts

hacks

I am using a readonly modalkit TextBox for the display and added a hacky workaround to update the content without loosing cursor position and to add styling to the textbox.

fn render(mut self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let lines = state.render(&mut self, area.width as usize);
// update text
let buffer = state.tbox.buffer();
let mut locked = buffer.write().unwrap();
let mut text = EditRope::default();
for (line, _) in lines.iter() {
for span in line {
text += span.content.as_ref().into();
}
text += '\n'.into();
}
locked.text = text;
std::mem::drop(locked);
// only store style info
let lines: Vec<_> = lines
.into_iter()
.map(|(line, preview)| {
let styles: Vec<(_, u16)> = line
.iter()
.map(|span| {
let width = UnicodeSegmentation::graphemes(span.content.as_ref(), true)
.filter(|symbol| !symbol.contains(|char: char| char.is_control()))
.map(|symbol| symbol.width() as u16)
.sum();
(span.style, width)
})
.collect();
(line.style, styles, preview)
})
.collect();
// draw text
state.draw(area, buf, self.focused, self.store);
// set highlighting
let mut image_previews = vec![];
let mut draw_lines = lines.into_iter().fuse().skip(state.tbox.viewctx.corner.y);
let draw_area = area.intersection(buf.area);
for y in draw_area.top()..draw_area.top() + draw_area.height {
let mut x = draw_area.left();
if let Some((line_style, styles, line_preview)) = draw_lines.next() {
if let Some((backend, msg_x)) = line_preview {
image_previews.push((x + msg_x, y, backend));
}
for (style, width) in styles {
let remaining_width = draw_area.right().saturating_sub(x);
for i in 0..remaining_width.min(width) {
let old_style = buf[(x + i, y)].style();
let new_style = old_style.patch(line_style).patch(style);
buf[(x + i, y)].set_style(new_style);
}
x += width;
}
}
}
// Render image previews after all text lines have been drawn, as the render might draw below the current
// line.
for (x, y, backend) in image_previews {
let image_widget = Image::new(&backend);
let mut rect = backend.area();
rect.x = x;
rect.y = y;
// Don't render outside of scrollback area
if rect.bottom() <= area.bottom() && rect.right() <= area.right() {
image_widget.render(rect, buf);
}
}
}

To implement this I had to use VAWVAW/modalkit@6bbe8a4 which sets a few struct member to pub.

I'm not sure whether it is worth to upstream this to modalkit completely or add a few wrappers to allow safe access to these members.

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 this pull request may close these issues.

1 participant