feat(axum): add safe html page composition

Add a SafeHtml PageRequest path for shell/partial composition and migrate examples so rendered hemplate fragments stay typed through the transport boundary instead of round-tripping through unchecked strings.

req: axum_integration/001

req: html_safety/001

req: html_safety/002
This commit is contained in:
slhx agent
2026-06-01 23:45:52 +02:00
parent a287d87e38
commit 4e47b470a7
6 changed files with 96 additions and 49 deletions
+10 -10
View File
@@ -111,7 +111,7 @@ async fn main() {
async fn home(State(state): State<Arc<AppState>>, request: PageRequest) -> impl IntoResponse {
let board = state.board.lock().unwrap().clone();
request
.page(page_html(&board), shell)
.page_html(page_html(&board), shell)
.title("slhx Kanban")
.fingerprint(ui::BUILD_FINGERPRINT)
}
@@ -223,18 +223,18 @@ fn parse_column(value: Option<&str>) -> usize {
.unwrap_or(0)
}
fn page_html(board: &BoardState) -> String {
// Explicit full-page composition boundary for already-rendered hemplate fragments.
fn page_html(board: &BoardState) -> SafeHtml {
// req: html_safety/002 req: view/001
board_ui::render(&Board {
options: render_options(),
board: ui::render(&board_view(board)),
})
.into_string()
}
fn shell(body: String) -> String {
format!(
fn shell(body: SafeHtml) -> SafeHtml {
// Explicit full-page composition boundary for already-rendered hemplate fragments.
// req: html_safety/001 req: html_safety/002 req: axum_integration/001
SafeHtml::trusted(format!(
r#"<!doctype html>
<html lang="en">
<head>
@@ -257,7 +257,7 @@ fn shell(body: String) -> String {
{body}
</body>
</html>"#
)
))
}
fn render_options() -> SafeHtml {
@@ -311,10 +311,10 @@ mod tests {
#[test]
fn kanban_page_is_composed_by_a_hemplate_view() {
let html = page_html(&BoardState::default());
assert!(!html.contains("__OPTIONS__"));
assert!(!html.contains("__BOARD__"));
assert!(!html.as_str().contains("__OPTIONS__"));
assert!(!html.as_str().contains("__BOARD__"));
let document = Html::parse_fragment(&html);
let document = Html::parse_fragment(html.as_str());
assert_eq!(document.select(&selector("section[data-slhx-root=\"kanban\"]")).count(), 1);
assert_eq!(document.select(&selector("select[name=\"column\"] > option")).count(), 3);
assert_eq!(document.select(&selector("[data-sid]")).count(), 3);