use super::super::*;
use super::geometry::node_left_top;
use super::math_label::{sequence_katex_label, write_sequence_katex_foreign_object};
use crate::sequence::{
SEQUENCE_LEFT_OF_NOTE_FINAL_WRAP_SLACK_PX, SEQUENCE_NOTE_WRAP_SLACK_PX, SequenceMathHeightMode,
sequence_text_line_step_px,
};
use merman_core::diagrams::sequence::SequenceMessage;
use rustc_hash::FxHashMap;
pub(super) struct SequenceNoteRenderContext<'a> {
pub(super) nodes_by_id: &'a FxHashMap<&'a str, &'a LayoutNode>,
pub(super) measurer: &'a dyn TextMeasurer,
pub(super) actor_label_font_size: f64,
pub(super) wrap_padding: f64,
pub(super) note_text_style: &'a TextStyle,
pub(super) sanitize_config: &'a merman_core::MermaidConfig,
pub(super) math_renderer: Option<&'a (dyn crate::math::MathRenderer + Send + Sync)>,
}
pub(super) fn render_sequence_note(
out: &mut String,
msg: &SequenceMessage,
ctx: &SequenceNoteRenderContext<'_>,
) {
if msg.message_type != 2 {
return;
}
let id = &msg.id;
let raw = msg.message_text();
let node_id = format!("note-{id}");
let Some(n) = ctx.nodes_by_id.get(node_id.as_str()).copied() else {
return;
};
let (x, y) = node_left_top(n);
let cx = x + (n.width / 2.0);
let text_y = y + 5.0;
let line_step = sequence_text_line_step_px(ctx.actor_label_font_size);
out.push_str(r#"<g>"#);
let _ = write!(
&mut *out,
r##"<rect x="{x}" y="{y}" fill="#EDF2AE" stroke="#666" width="{w}" height="{h}" class="note"/>"##,
x = fmt(x),
y = fmt(y),
w = fmt(n.width),
h = fmt(n.height)
);
if let Some(katex) = sequence_katex_label(
raw,
ctx.note_text_style,
ctx.sanitize_config,
ctx.math_renderer,
SequenceMathHeightMode::Draw,
) {
write_sequence_katex_foreign_object(
out,
&katex,
(x + n.width / 2.0 - katex.width / 2.0).round(),
(y + n.height / 2.0 - katex.height / 2.0).round(),
);
} else if msg.wrap {
let placement = msg.placement.unwrap_or(2);
let wrap_slack = if placement == 0 {
SEQUENCE_LEFT_OF_NOTE_FINAL_WRAP_SLACK_PX
} else {
SEQUENCE_NOTE_WRAP_SLACK_PX
};
let wrap_w = (n.width - 2.0 * ctx.wrap_padding + wrap_slack).max(1.0);
let lines = crate::text::wrap_label_like_mermaid_lines_floored_bbox(
raw,
ctx.measurer,
ctx.note_text_style,
wrap_w,
);
render_sequence_note_lines(
out,
lines.iter().map(String::as_str),
cx,
text_y,
line_step,
ctx.actor_label_font_size,
);
} else {
render_sequence_note_lines(
out,
crate::text::split_html_br_lines(raw),
cx,
text_y,
line_step,
ctx.actor_label_font_size,
);
}
out.push_str("</g>");
}
fn render_sequence_note_lines<'a>(
out: &mut String,
lines: impl IntoIterator<Item = &'a str>,
cx: f64,
text_y: f64,
line_step: f64,
actor_label_font_size: f64,
) {
for (i, line) in lines.into_iter().enumerate() {
let decoded = merman_core::entities::decode_mermaid_entities_to_unicode(line);
let y = text_y + (i as f64) * line_step;
let _ = write!(
&mut *out,
r#"<text x="{x}" y="{y}" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: {fs}px; font-weight: 400;"><tspan x="{x}">{text}</tspan></text>"#,
x = fmt(cx),
y = fmt(y),
fs = fmt(actor_label_font_size),
text = escape_xml(decoded.as_ref())
);
}
}