A call to Jev as a window a visitor can read: the request and the response, printed from the raw bodies, indented and coloured, and a probability drawn as lmjtfy draws it. This is lmjtfy's tool-call panel, lifted out so another site shows its own calls the same way.
The rule it keeps (lmjtfy's, 2026-10-02): printing may add whitespace and nothing else. No
reordered keys, no dropped or summarised fields. A site's tests parse what is printed back and
compare it to the body it holds (printed_json here does the parsing).
12use crate::components::{Icon, pixel_icon};
A JSON body, indented and coloured for reading. Only whitespace is added: keys keep their order and every value is printed as it was sent. Text that is not JSON is shown as it is.
23fn node(value: &Value, depth: usize) -> Markup { 24 let indent = " ".repeat(depth + 1); 25 let close = " ".repeat(depth); 26 match value { 27 Value::Object(map) if !map.is_empty() => html! { 28 "{\n" 29 @for (i, (key, child)) in map.iter().enumerate() { 30 (indent) span .k { (quoted(key)) } ": " (node(child, depth + 1)) 31 @if i + 1 < map.len() { "," } "\n" 32 } 33 (close) "}" 34 }, 35 Value::Array(items) if !items.is_empty() => html! { 36 "[\n" 37 @for (i, child) in items.iter().enumerate() { 38 (indent) (node(child, depth + 1)) 39 @if i + 1 < items.len() { "," } "\n" 40 } 41 (close) "]" 42 }, 43 Value::String(text) => html! { span .s { (quoted(text)) } }, 44 // Numbers, booleans, null, and the empty object and array. 45 other => html! { span .n { (other.to_string()) } }, 46 } 47}
A string as JSON writes it, quotes and escapes included.
One labelled block of a call: an optional plain head (the request line and headers) over a body.
55pub struct Panel<'a> {
request, response.
57 pub label: &'a str,
Lines shown as they are above the body, as POST /v1/systemone.
A line after the body, marked with a !: what the reader should know about this panel
(a call that was not sent, and why).
A call as a window that folds (open or shut): a title bar (title, and meta at the right) over its
panels.
exchange with a title and a meta that are markup (a moment the page rewrites as "3 minutes
ago", a name in a link), and lead, whatever the site puts first in the body, above the
panels (a choice between earlier answers to the same request).
75pub fn exchange_with(icon: &Icon, title: Markup, meta: Option<Markup>, open: bool, lead: Markup, panels: &[Panel<'_>]) -> Markup { 76 html! { 77 details .win open[open] { 78 summary .bar-title { 79 span .fold {} (pixel_icon(icon, "")) (title) 80 @if let Some(meta) = meta { span .meta { (meta) } } 81 } 82 div .body { 83 (lead) 84 @for panel in panels { 85 div .wire { 86 span .label-s { (panel.label) } 87 @if let Some(head) = panel.head { pre { (head) } } 88 (json(panel.body)) 89 @if let Some(note) = panel.note { p .note { (note) } } 90 } 91 } 92 } 93 } 94 } 95}
One option of a choice (or the one side of a yes-or-no) and its probability.
Where the square sits on its rule, from a probability.
Probabilities as lmjtfy draws them: an inverted label, the figure at the right, and a rule with
a square at the value. threshold draws a tick on every rule where a decision is made on it,
named threshold_label.
117pub fn bars(bars: &[Bar<'_>], threshold: Option<(f64, &str)>) -> Markup { 118 html! { 119 div .bars { 120 @for bar in bars { 121 div .bar.won[bar.won] { 122 span .label { (bar.label) } 123 span .pct { (percent(bar.p)) } 124 span .track { 125 @if let Some((t, name)) = threshold { span .cut style=(at(t)) title=(name) {} } 126 span .thumb style=(at(bar.p)) {} 127 } 128 } 129 } 130 } 131 } 132}
The JSON a panel printed, read back out of its markup: the text with tags gone and entities undone. A site's test compares it to the body it passed in.
136pub fn printed_json(markup: &str) -> Option<Value> { 137 let mut text = String::new(); 138 let mut in_tag = false; 139 for ch in markup.chars() { 140 match ch { 141 '<' => in_tag = true, 142 '>' => in_tag = false, 143 c if !in_tag => text.push(c), 144 _ => {} 145 } 146 } 147 let text = text.replace("<", "<").replace(">", ">").replace(""", "\"").replace("&", "&"); 148 serde_json::from_str(&text).ok() 149}
151#[cfg(test)] 152mod tests { 153 use super::*; 154 use crate::components::icons; 155 156 const RAW: &str = r#"{"model":"m","questions":{"b":{"type":"noul","instructions":"Is <it> \"ok\" & fine?"}},"state":{"z":1,"a":[true,null,"x"],"e":{},"l":[]}}"#; 157 158 #[test] 159 fn what_is_printed_is_the_body_with_whitespace_added_and_nothing_else() { 160 let html = json(RAW).into_string(); 161 let back = printed_json(&html).expect("the printed text is JSON"); 162 let sent: Value = serde_json::from_str(RAW).unwrap(); 163 assert_eq!(back, sent); 164 // Keys are in the order they were sent, not sorted. 165 assert_eq!(serde_json::to_string(&back).unwrap(), RAW); 166 assert!(html.contains("<span class=\"k\">")); 167 } 168 169 #[test] 170 fn text_that_is_not_json_is_shown_as_it_is() { 171 assert_eq!(json("upstream <down>").into_string(), "<pre>upstream <down></pre>"); 172 } 173 174 #[test] 175 fn a_call_is_a_window_with_a_labelled_panel_for_each_body() { 176 let html = exchange( 177 &icons::JEV, 178 "jev · 2 questions", 179 "200", 180 true, 181 &[Panel { label: "request", head: Some("POST /v1/systemone"), body: RAW, note: None }, Panel { label: "response", head: None, body: "{\"a\":1}", note: Some("kept") }], 182 ) 183 .into_string(); 184 assert!(html.starts_with("<details class=\"win\" open>")); 185 assert!(html.contains("<span class=\"label-s\">request</span>") && html.contains("<span class=\"label-s\">response</span>")); 186 assert!(html.contains("<pre>POST /v1/systemone</pre>")); 187 assert!(html.find("request").unwrap() < html.find("response").unwrap()); 188 assert!(html.contains("<p class=\"note\">kept</p>") && html.matches("class=\"note\"").count() == 1); 189 } 190 191 #[test] 192 fn a_call_may_lead_with_markup_and_carry_markup_in_its_title_bar() { 193 let html = exchange_with(&icons::TOOL, html! { "tool call · " b { "x" } }, Some(html! { i { "now" } }), false, html! { div #lead {} }, &[]).into_string(); 194 assert!(html.starts_with("<details class=\"win\"><summary class=\"bar-title\">"), "{html}"); 195 assert!(html.contains("tool call · <b>x</b>") && html.contains("<span class=\"meta\"><i>now</i></span>")); 196 assert!(html.find("id=\"lead\"").unwrap() > html.find("class=\"body\"").unwrap()); 197 } 198 199 #[test] 200 fn a_probability_is_a_square_on_a_rule_and_a_threshold_is_a_tick_on_it() { 201 let html = bars(&[Bar { label: "yes", p: 0.9, won: true }, Bar { label: "no", p: 0.1, won: false }], Some((0.85, "allowed from here"))).into_string(); 202 assert!(html.contains("<span class=\"pct\">90%</span>") && html.contains("left: 90.0%")); 203 assert_eq!(html.matches("class=\"cut\"").count(), 2); 204 assert!(html.contains("left: 85.0%") && html.contains("title=\"allowed from here\"")); 205 assert!(html.contains("class=\"bar won\"")); 206 assert!(!bars(&[Bar { label: "x", p: 2.0, won: false }], None).into_string().contains("left: 200")); 207 } 208}