feat(build): accept domain ids on keyed targets

Let generated string-keyed targets accept displayable domain ids directly, removing caller-side to_string noise from keyed partial append/replace flows.

req: codegen/002

req: dx/006
This commit is contained in:
slhx agent
2026-06-02 07:34:00 +02:00
parent dfd8020617
commit 339ca769fb
3 changed files with 8 additions and 6 deletions
+1 -1
View File
@@ -226,7 +226,7 @@ a `data-*` handle param is statically known or runtime-extracted.
001 `slhx_build` generates three artifacts from the generic Surface IR: (a) `slhx.generated.rs` containing ergonomic resource modules (`slots`, `targets`, `handles`, `forms`, `atoms`), (b) `slhx.syms` for proc-macro validation, (c) runtime id-lowering tables. `slhx_build` interprets tool-specific conventions (`data-slhx-*`, `h-for`, `h-key`, form controls) from the Surface. [north_star] 001 `slhx_build` generates three artifacts from the generic Surface IR: (a) `slhx.generated.rs` containing ergonomic resource modules (`slots`, `targets`, `handles`, `forms`, `atoms`), (b) `slhx.syms` for proc-macro validation, (c) runtime id-lowering tables. `slhx_build` interprets tool-specific conventions (`data-slhx-*`, `h-for`, `h-key`, form controls) from the Surface. [north_star]
### req: codegen/002 ### req: codegen/002
002 Generated view modules expose ergonomic target objects and resource commands: `targets::list.put(value)`, `targets::row.append(key, value)`, `targets::row.replace(key, value)`, plus compatibility commands `put(slot, value)`, `append(keyed_slot, key, value)`, `prepend(keyed_slot, key, value)`, and `replace(keyed_slot, key, value)`. Commands return `impl IntoEffect` and preserve generated lowering. 002 Generated view modules expose ergonomic target objects and resource commands: `targets::list.put(value)`, `targets::row.append(key, value)`, `targets::row.replace(key, value)`, plus compatibility commands `put(slot, value)`, `append(keyed_slot, key, value)`, `prepend(keyed_slot, key, value)`, and `replace(keyed_slot, key, value)`. String-keyed target objects accept displayable domain ids without caller-side `.to_string()` noise. Commands return `impl IntoEffect` and preserve generated lowering.
### req: codegen/003 ### req: codegen/003
003 Generated module `handles` exports typed constants: `Handle<I>` where `I` is `Form<T>`, a param type, or `()`. Users rarely reference handles directly; they are consumed by `#[slhx::handler]` for validation. 003 Generated module `handles` exports typed constants: `Handle<I>` where `I` is `Form<T>`, a param type, or `()`. Users rarely reference handles directly; they are consumed by `#[slhx::handler]` for validation.
+1 -1
View File
@@ -77,7 +77,7 @@ mod tests {
fn todos_append_keyed_rows_from_form_input() { fn todos_append_keyed_rows_from_form_input() {
fn add_todo(input: TodoInput) -> impl IntoEffect { fn add_todo(input: TodoInput) -> impl IntoEffect {
let todo = Todo { id: 7, title: input.title }; let todo = Todo { id: 7, title: input.title };
todos::targets::todo_row.append(todo.id.to_string(), &TodoRow { title: todo.title }) todos::targets::todo_row.append(todo.id, &TodoRow { title: todo.title })
} }
let effect = inspect(add_todo(TodoInput { title: "Ship v0".into() })); let effect = inspect(add_todo(TodoInput { title: "Ship v0".into() }));
+6 -4
View File
@@ -426,10 +426,12 @@ impl Resources {
out.push_str(&format!("{inner} pub const fn new(slot: ::slhx::KeyedSlot<K, T>) -> Self {{ Self {{ slot }} }}\n")); out.push_str(&format!("{inner} pub const fn new(slot: ::slhx::KeyedSlot<K, T>) -> Self {{ Self {{ slot }} }}\n"));
out.push_str(&format!("{inner} pub const fn slot(self) -> ::slhx::KeyedSlot<K, T> {{ self.slot }}\n")); out.push_str(&format!("{inner} pub const fn slot(self) -> ::slhx::KeyedSlot<K, T> {{ self.slot }}\n"));
out.push_str(&format!("{inner} pub const fn id(self) -> ::slhx::ResourceId {{ self.slot.id() }}\n")); out.push_str(&format!("{inner} pub const fn id(self) -> ::slhx::ResourceId {{ self.slot.id() }}\n"));
out.push_str(&format!("{inner} pub fn append(self, key: K, view: &impl ::hemplate::Hemplate) -> impl ::slhx::IntoEffect {{ super::append(self.slot, key, view) }}\n")); out.push_str(&format!("{inner}}}\n"));
out.push_str(&format!("{inner} pub fn prepend(self, key: K, view: &impl ::hemplate::Hemplate) -> impl ::slhx::IntoEffect {{ super::prepend(self.slot, key, view) }}\n")); out.push_str(&format!("{inner}impl<T> KeyedSlotTarget<::std::string::String, T> {{\n"));
out.push_str(&format!("{inner} pub fn replace(self, key: K, view: &impl ::hemplate::Hemplate) -> impl ::slhx::IntoEffect {{ super::replace(self.slot, key, view) }}\n")); out.push_str(&format!("{inner} pub fn append(self, key: impl ::std::string::ToString, view: &impl ::hemplate::Hemplate) -> impl ::slhx::IntoEffect {{ self.slot.append_html(key.to_string(), super::render(view)) }}\n"));
out.push_str(&format!("{inner} pub fn remove(self, key: K) -> impl ::slhx::IntoEffect {{ self.slot.remove(key) }}\n")); out.push_str(&format!("{inner} pub fn prepend(self, key: impl ::std::string::ToString, view: &impl ::hemplate::Hemplate) -> impl ::slhx::IntoEffect {{ self.slot.prepend_html(key.to_string(), super::render(view)) }}\n"));
out.push_str(&format!("{inner} pub fn replace(self, key: impl ::std::string::ToString, view: &impl ::hemplate::Hemplate) -> impl ::slhx::IntoEffect {{ self.slot.replace_html(key.to_string(), super::render(view)) }}\n"));
out.push_str(&format!("{inner} pub fn remove(self, key: impl ::std::string::ToString) -> impl ::slhx::IntoEffect {{ self.slot.remove(key.to_string()) }}\n"));
out.push_str(&format!("{inner}}}\n")); out.push_str(&format!("{inner}}}\n"));
for res in self.slots.values().filter(|res| component_matches(res, component)) { for res in self.slots.values().filter(|res| component_matches(res, component)) {
if res.keyed { if res.keyed {