From f7d9f820e23edd3055224b0f66bad74daef54421 Mon Sep 17 00:00:00 2001 From: slhx agent Date: Mon, 1 Jun 2026 23:15:31 +0200 Subject: [PATCH] feat(slhx): add concise public render helper Expose slhx::render(view) as the short SafeHtml composition helper and keep render_html(view) as a compatibility alias, matching generated modules' lean page-building path. req: dx/006 req: html_safety/002 --- REQUIREMENTS.md | 2 +- slhx/src/lib.rs | 35 +++++++++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md index bc60680..97b80d8 100644 --- a/REQUIREMENTS.md +++ b/REQUIREMENTS.md @@ -76,7 +76,7 @@ slhx competes with React by making frontend frameworks unnecessary for most apps 005 Error messages must explain fixes in author language, not internal language. Say “add `h-key="todo.id"` to this `h-for`”, not “missing ScopeKey for ResourceRef”. ### req: dx/006 -006 Generated resource methods are the preferred authoring API: `slots::todo_list.render(view)`, `slots::card.replace(key, view)`, `slots::count.text(42)`, `atoms::user.set(user)`. Generated view modules also expose `render(view)` for trusted hemplate-to-`SafeHtml` page and fragment composition; `render_html(view)` remains as an explicit compatibility alias. These return `impl IntoEffect` or `SafeHtml` at the boundary. Raw `Effect` constructors, opcodes, and `EffectWriter` remain low-level. [north_star] +006 Generated resource methods are the preferred authoring API: `slots::todo_list.render(view)`, `slots::card.replace(key, view)`, `slots::count.text(42)`, `atoms::user.set(user)`. The public facade and generated view modules expose `render(view)` for trusted hemplate-to-`SafeHtml` page and fragment composition; `render_html(view)` remains as an explicit compatibility alias. These return `impl IntoEffect` or `SafeHtml` at the boundary. Raw `Effect` constructors, opcodes, and `EffectWriter` remain low-level. [north_star] ### req: dx/007 007 Tuple composition of `IntoEffect` is the canonical batch syntax: `(a, b, c)` implements `IntoEffect` up to arity 12. `Effect::batch((...))` is available but not required for the happy path. diff --git a/slhx/src/lib.rs b/slhx/src/lib.rs index 2b4a892..26b2281 100644 --- a/slhx/src/lib.rs +++ b/slhx/src/lib.rs @@ -6,13 +6,17 @@ pub use slhx_core::*; pub use slhx_derive::{app, component, form, handler, surface}; -pub fn render_html(view: &impl hemplate::Hemplate) -> SafeHtml { +pub fn render(view: &impl hemplate::Hemplate) -> SafeHtml { let mut html = String::new(); view.render_into(&mut html) .expect("hemplate view renders into slhx effect payload"); SafeHtml::trusted(html) } +pub fn render_html(view: &impl hemplate::Hemplate) -> SafeHtml { + render(view) +} + pub trait RenderSlotExt { fn render(self, view: &impl hemplate::Hemplate) -> Effect; @@ -26,7 +30,7 @@ pub trait RenderSlotExt { impl RenderSlotExt for Slot { fn render(self, view: &impl hemplate::Hemplate) -> Effect { - self.html(render_html(view)) + self.html(render(view)) } } @@ -62,15 +66,15 @@ where K: ToString, { fn append(self, key: K, view: &impl hemplate::Hemplate) -> Effect { - self.append_html(key, render_html(view)) + self.append_html(key, render(view)) } fn prepend(self, key: K, view: &impl hemplate::Hemplate) -> Effect { - self.prepend_html(key, render_html(view)) + self.prepend_html(key, render(view)) } fn replace(self, key: K, view: &impl hemplate::Hemplate) -> Effect { - self.replace_html(key, render_html(view)) + self.replace_html(key, render(view)) } } @@ -80,6 +84,25 @@ pub mod prelude { CssClasses, Effect, EventName, Form, FormModel, FormValue, Handle, IntoEffect, KeyedSlot, ParamName, Slot, }; - pub use crate::{render_html, RenderKeyedSlotExt, RenderSlotExt}; + pub use crate::{render, render_html, RenderKeyedSlotExt, RenderSlotExt}; pub use slhx_derive::{app, component, form, handler, surface}; } + +#[cfg(test)] +mod tests { + struct InlineView; + + impl hemplate::Hemplate for InlineView { + fn render_into(&self, out: &mut String) -> Result<(), hemplate::error::HemplateError> { + out.push_str("ok"); + Ok(()) + } + } + + #[test] + fn render_is_the_short_safe_html_helper() { + // req: dx/006 req: html_safety/002 + assert_eq!(crate::render(&InlineView).as_str(), "ok"); + assert_eq!(crate::render_html(&InlineView).as_str(), "ok"); + } +}