mod support;
use macroquad::prelude::*;
use rae::{
materialize_output, CodeTokenKind, OutputDocument, OutputOptions, OutputRow, Palette, Rect,
Rgba,
};
use support::*;
fn window_conf() -> Conf {
Conf {
window_title: "rae code output".to_string(),
window_width: 1440,
window_height: 900,
high_dpi: true,
sample_count: 4,
..Default::default()
}
}
#[macroquad::main(window_conf)]
async fn main() {
let palette = Palette::midnight();
let doc = materialize_output(
assistant_output(),
OutputOptions::for_width(82).with_code_line_numbers(true),
);
let max_frames = max_frames_from_env();
let mut frames = 0u64;
loop {
frames = frames.saturating_add(1);
if should_stop(frames, max_frames) {
break;
}
let time = get_time() as f32;
let viewport = Rect::new(0.0, 0.0, screen_width(), screen_height());
draw_scene_backdrop(&palette, time);
draw_header(viewport, &palette, &doc);
draw_output_document(viewport, &palette, &doc);
draw_copy_panel(viewport, &palette, &doc);
next_frame().await;
}
}
fn assistant_output() -> &'static str {
r#"# Patch
Here is a minimal safe code output. The incoming text can contain ANSI like [31mred[0m or OSC links.
```rust
pub fn greet(name: &str) -> String {
// renderer gets sanitized tokens, not raw terminal escapes
format!("hello {name}")
}
```
- Strip ANSI before rendering.
- Preserve copy-safe fenced code.
- Keep line numbers and syntax token classes renderer-neutral.
"#
}
fn draw_header(viewport: Rect, palette: &Palette, doc: &OutputDocument) {
let header = Rect::new(28.0, 24.0, viewport.width - 56.0, 74.0);
draw_glass_panel(header, 26.0, palette, palette.accent, true);
draw_text_line(
"Code Output",
header.x + 26.0,
header.y + 42.0,
31.0,
to_mq(palette.text),
);
draw_text_line(
"sanitized Markdown/code rows with renderer-neutral token classes",
header.x + 248.0,
header.y + 40.0,
17.0,
to_mq(palette.muted),
);
draw_pill(
Rect::new(header.right() - 376.0, header.y + 20.0, 142.0, 32.0),
&format!("{} rows", doc.rows.len()),
palette.success,
palette.text,
);
draw_pill(
Rect::new(header.right() - 220.0, header.y + 20.0, 198.0, 32.0),
&format!("{} escapes removed", doc.removed_escape_sequences),
palette.warning,
palette.text,
);
}
fn draw_output_document(viewport: Rect, palette: &Palette, doc: &OutputDocument) {
let rect = Rect::new(28.0, 126.0, viewport.width * 0.66, viewport.height - 154.0);
draw_glass_panel(rect, 30.0, palette, palette.success, true);
draw_section_header(
"RENDERED OUTPUT ROWS",
Rect::new(rect.x + 26.0, rect.y + 38.0, rect.width - 52.0, 18.0),
palette.success,
palette.muted,
);
let mut y = rect.y + 78.0;
let left = rect.x + 30.0;
let width = rect.width - 60.0;
let mut in_code = false;
for row in doc.visible_rows(0, 22) {
match row {
OutputRow::Blank => y += 14.0,
OutputRow::Heading { level, text } => {
draw_text_line(&format!("H{level}"), left, y, 13.0, to_mq(palette.accent));
draw_text_line(text, left + 42.0, y, 24.0, to_mq(palette.text));
y += 36.0;
}
OutputRow::Paragraph { text, .. } => {
y = draw_wrapped_text(
text,
left + 42.0,
y,
width - 42.0,
16.0,
22.0,
to_mq(palette.text),
);
}
OutputRow::Bullet { text, .. } => {
draw_text_line("•", left + 8.0, y, 19.0, to_mq(palette.warning));
y = draw_wrapped_text(
text,
left + 42.0,
y,
width - 42.0,
16.0,
22.0,
to_mq(palette.text),
);
}
OutputRow::Quote { text, .. } => {
draw_line(
left + 8.0,
y - 16.0,
left + 8.0,
y + 10.0,
2.0,
to_mq(palette.accent),
);
y = draw_wrapped_text(
text,
left + 28.0,
y,
width - 28.0,
16.0,
22.0,
to_mq(palette.muted),
);
}
OutputRow::CodeFenceStart { language } => {
in_code = true;
draw_pill(
Rect::new(left, y - 18.0, 104.0, 26.0),
language,
palette.accent,
palette.text,
);
y += 18.0;
}
OutputRow::Code { gutter, tokens, .. } => {
let line_rect = Rect::new(left, y - 18.0, width, 26.0);
draw_rounded_rect(
line_rect,
8.0,
rgba8(1, 5, 16, if in_code { 150 } else { 80 }),
);
draw_text_line(
gutter.as_deref().unwrap_or(""),
left + 12.0,
y,
14.0,
to_mq(palette.muted),
);
let mut x = left + 68.0;
for token in tokens {
let color = token_color(token.kind, palette);
draw_text_line(&token.text, x, y, 14.0, to_mq(color));
x += measure_text(&token.text, None, 14, 1.0).width;
}
y += 29.0;
}
OutputRow::CodeFenceEnd => {
in_code = false;
y += 12.0;
}
}
}
}
fn draw_copy_panel(viewport: Rect, palette: &Palette, doc: &OutputDocument) {
let rect = Rect::new(
viewport.width * 0.70,
126.0,
viewport.width * 0.30 - 28.0,
viewport.height - 154.0,
);
draw_glass_panel(rect, 30.0, palette, palette.warning, false);
draw_section_header(
"SANITIZATION",
Rect::new(rect.x + 24.0, rect.y + 38.0, rect.width - 48.0, 18.0),
palette.warning,
palette.muted,
);
let metric_w = (rect.width - 66.0) / 2.0;
draw_metric(
Rect::new(rect.x + 24.0, rect.y + 78.0, metric_w, 70.0),
"escapes",
&doc.removed_escape_sequences.to_string(),
palette.warning,
palette,
);
draw_metric(
Rect::new(rect.x + 42.0 + metric_w, rect.y + 78.0, metric_w, 70.0),
"controls",
&doc.removed_control_chars.to_string(),
palette.accent,
palette,
);
draw_metric(
Rect::new(rect.x + 24.0, rect.y + 164.0, metric_w, 70.0),
"zero-width",
&doc.removed_zero_width_chars.to_string(),
palette.success,
palette,
);
draw_metric(
Rect::new(rect.x + 42.0 + metric_w, rect.y + 164.0, metric_w, 70.0),
"truncated",
if doc.truncated { "yes" } else { "no" },
Rgba::rgb8(205, 130, 255),
palette,
);
let copy = Rect::new(
rect.x + 24.0,
rect.y + 262.0,
rect.width - 48.0,
rect.height - 286.0,
);
draw_rounded_rect(copy, 20.0, rgba8(1, 5, 16, 150));
draw_text_line(
"copy-safe markdown",
copy.x + 16.0,
copy.y + 28.0,
16.0,
to_mq(palette.accent),
);
draw_wrapped_text(
&doc.copy_text,
copy.x + 16.0,
copy.y + 58.0,
copy.width - 32.0,
13.0,
18.0,
to_mq(palette.muted),
);
}
fn token_color(kind: CodeTokenKind, palette: &Palette) -> Rgba {
match kind {
CodeTokenKind::Text => palette.text,
CodeTokenKind::Keyword => palette.accent,
CodeTokenKind::String => palette.success,
CodeTokenKind::Number => palette.warning,
CodeTokenKind::Comment => palette.muted,
CodeTokenKind::Punctuation => Rgba::rgb8(205, 130, 255),
}
}