Randomly colorize mxids
Some checks failed
CI / Build and test (, nightly) (push) Waiting to run
CI / lint (push) Has been cancelled
CI / Build and test (, 1.73.0) (push) Has been cancelled
CI / Build and test (, beta) (push) Has been cancelled

This commit is contained in:
2023-11-26 04:11:23 +01:00
parent ab98d565dc
commit 7957980ff7
4 changed files with 72 additions and 20 deletions

View File

@ -52,7 +52,7 @@ use tokio::sync::oneshot;
use super::{Buffer, BufferId, BufferItem, BufferItemContent, BufferSortKey, FullyReadStatus};
use crate::config::Config;
use crate::html::{escape_html, format_html};
use crate::html::{escape_html, format_html, markup_colored_by_mxid};
use crate::widgets::Prerender;
/// Like [`BufferItemContent`] but owned.
@ -98,6 +98,7 @@ impl DynamicUsage for OwnedBufferItemContent {
}
pub struct SingleClientRoomBuffer {
config: Arc<Config>,
room_id: OwnedRoomId,
client: Client,
@ -178,7 +179,7 @@ impl SingleClientRoomBuffer {
OwnedBufferItemContent::Text {
event_id: event.event_id().map(ToOwned::to_owned),
is_message: false,
text: format_html(&format!($($tokens)*))
text: format_html(&self.config, &format!($($tokens)*))
}
}
}
@ -189,7 +190,7 @@ impl SingleClientRoomBuffer {
OwnedBufferItemContent::Text {
event_id: event.event_id().map(ToOwned::to_owned),
is_message: true,
text: format_html(&format!($($tokens)*))
text: format_html(&self.config, &format!($($tokens)*))
}
}
}
@ -201,6 +202,7 @@ impl SingleClientRoomBuffer {
.strip_prefix('@')
.expect("missing @ prefix"),
);
let sender = markup_colored_by_mxid(&sender, &sender);
match event.content() {
Message(message) => match message.msgtype() {
MessageType::Text(TextMessageEventContent {
@ -293,6 +295,7 @@ impl SingleClientRoomBuffer {
.strip_prefix('@')
.expect("missing @ prefix"),
);
let target = markup_colored_by_mxid(&target, &target);
let Some(change_kind) = change.change() else {
return text!("--- {} made incomprehensible changes to {}", sender, target);
};
@ -480,6 +483,7 @@ impl RoomBuffer {
items
);
self.buffers.push(SingleClientRoomBuffer {
config: self.config.clone(),
room_id: self.room_id.clone(),
client,
timeline: Arc::new(timeline),

View File

@ -147,9 +147,15 @@ pub struct BuflistStylesConfig {
pub uneventful: StyleConfig,
}
#[derive(Clone, Debug, Deserialize)]
pub struct UserStylesConfig {
pub rotation: Vec<StyleConfig>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct StylesConfig {
pub buflist: BuflistStylesConfig,
pub users: UserStylesConfig,
}
#[derive(Clone, Debug, Deserialize)]

View File

@ -61,17 +61,20 @@ unread_event = "bold"
uneventful = ""
# Style of usernames in chat logs
[style.user.color]
# Colors that can be assigned to users' names, picked semi-randomly
colors = [
[style.users]
# Colors that can be assigned to users' names, picked semi-randomly.
# Set an empty array to disable name coloration
rotation = [
"cyan",
"magenta",
"red",
"green",
"brown",
"lightblue",
"default",
"lightcyan",
"lightmagenta",
"lightgreen",
"blue",
"magenta",
"default",
"bold cyan",
"bold red",
"bold green",
"bold blue",
"bold magenta",
"bold default",
]

View File

@ -15,25 +15,50 @@
*/
use std::borrow::Cow;
use std::hash::{Hash, Hasher};
use std::rc::Rc;
use html5ever::driver::parse_fragment;
use html5ever::interface::QualName;
use html5ever::tendril::TendrilSink;
use html5ever::{local_name, namespace_url, ns, Attribute};
use html_escape::encode_text_minimal;
use markup5ever_rcdom::{Handle, Node, NodeData, RcDom};
use ratatui::style::{Color, Style, Stylize};
use ratatui::text::{Line, Span, Text};
use crate::config::Config;
pub fn escape_html<S: ?Sized + AsRef<str>>(s: &S) -> Cow<'_, str> {
encode_text_minimal(s)
html_escape::encode_text_minimal(s)
}
pub fn markup_colored_by_mxid<M: ?Sized + AsRef<str>, C: ?Sized + AsRef<str>>(
mxid: &M,
content: &C,
) -> String {
format!(
r#"<font data-ratatrix-colored-by-mxid="{}">{}</font>"#,
html_escape::encode_double_quoted_attribute(mxid),
content.as_ref()
)
}
fn count_digits(n: u16) -> u8 {
f32::log10(n.into()).floor() as u8 + 1
}
fn get_color_by_mxid(config: &Config, mxid: &str) -> Option<Style> {
let rotation = &config.style.users.rotation;
if rotation.is_empty() {
None
} else {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
mxid.hash(&mut hasher);
let color_id: usize = (hasher.finish() & (usize::MAX as u64)) as usize;
Some(*rotation[color_id % rotation.len()])
}
}
fn parse_hex_color(s: &str) -> Option<Color> {
// Should we implement a workaround for https://github.com/ratatui-org/ratatui/issues/475
// here, or wait for Crossterm to fix it?
@ -67,6 +92,7 @@ struct FormatState {
}
fn format_tree(
config: &Config,
tree: Rc<Node>,
state: &mut FormatState,
text: &mut Text<'static>,
@ -134,6 +160,11 @@ fn format_tree(
state.style.bg = Some(color);
}
},
"data-ratatrix-colored-by-mxid" => {
if let Some(style) = get_color_by_mxid(config, value.as_ref()) {
state.style = state.style.patch(style);
}
},
_ => {},
},
_ => {},
@ -247,12 +278,20 @@ fn format_tree(
},
_ => state.to_owned(),
},
Element { .. } => state.to_owned(), // Element not in the HTML namespace
Element { .. } => {
tracing::warn!("unknown element {:?}", tree.data);
state.to_owned()
}, // Element not in the HTML namespace
};
for subtree in tree.children.borrow().iter() {
previous_sibling_is_block =
format_tree(subtree.clone(), &mut state, text, previous_sibling_is_block);
previous_sibling_is_block = format_tree(
config,
subtree.clone(),
&mut state,
text,
previous_sibling_is_block,
);
}
match &tree.data {
@ -278,7 +317,7 @@ fn format_tree(
previous_sibling_is_block
}
pub fn format_html(s: &str) -> Text<'static> {
pub fn format_html(config: &Config, s: &str) -> Text<'static> {
let tree = parse_fragment(
RcDom::default(),
Default::default(),
@ -292,6 +331,6 @@ pub fn format_html(s: &str) -> Text<'static> {
..Default::default()
};
let mut text = Text::raw("");
format_tree(tree, &mut state, &mut text, false);
format_tree(config, tree, &mut state, &mut text, false);
text
}