refactor(techdemo): shorten generated resource paths

Import scoped generated modules at the page boundary so handlers and tests read as local slots/forms/handles/classes without hiding ownership or adding framework machinery.

req: component/003

req: dx/006
This commit is contained in:
slhx agent
2026-06-01 22:39:52 +02:00
parent f3706a440e
commit e9c717844d
2 changed files with 43 additions and 41 deletions
+14 -15
View File
@@ -3,7 +3,9 @@ pub mod ui {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::ui; use super::ui::control_center::{forms, handles, slots};
use super::ui::issue_card::handles as card_handles;
use super::ui::issue_lane::events as lane_events;
use hemplate::Hemplate; use hemplate::Hemplate;
use slhx::{Effect, IntoEffect, Payload, RenderSlotExt}; use slhx::{Effect, IntoEffect, Payload, RenderSlotExt};
use slhx_test::inspect; use slhx_test::inspect;
@@ -28,14 +30,14 @@ mod tests {
fn techdemo_uses_generated_slots_for_multi_target_updates() { fn techdemo_uses_generated_slots_for_multi_target_updates() {
fn update() -> impl IntoEffect { fn update() -> impl IntoEffect {
( (
ui::control_center::slots::hero_metrics.render(&FastMetric { label: "fast" }), slots::hero_metrics.render(&FastMetric { label: "fast" }),
ui::control_center::slots::notice.text("typed"), slots::notice.text("typed"),
) )
} }
let batch = inspect(update()); let batch = inspect(update());
assert!(batch.has_slot(ui::control_center::slots::hero_metrics)); assert!(batch.has_slot(slots::hero_metrics));
assert!(batch.has_slot(ui::control_center::slots::notice)); assert!(batch.has_slot(slots::notice));
} }
// req: examples/001 req: form/001 req: form/004 req: form/006 req: derive_handler/003 // req: examples/001 req: form/001 req: form/004 req: form/006 req: derive_handler/003
@@ -43,34 +45,31 @@ mod tests {
fn techdemo_form_handler_is_checked_against_hemplate_form() { fn techdemo_form_handler_is_checked_against_hemplate_form() {
#[slhx::handler] #[slhx::handler]
fn launch_work(_form: slhx::Form<LaunchWork>) -> impl IntoEffect { fn launch_work(_form: slhx::Form<LaunchWork>) -> impl IntoEffect {
ui::control_center::slots::notice.text("queued") slots::notice.text("queued")
} }
let batch = inspect(launch_work(LaunchWork::FORM)); let batch = inspect(launch_work(LaunchWork::FORM));
assert!(batch.has_slot(ui::control_center::slots::notice)); assert!(batch.has_slot(slots::notice));
assert!(matches!(batch.ops(), [Effect::Put { payload: Payload::Text(_), .. }])); assert!(matches!(batch.ops(), [Effect::Put { payload: Payload::Text(_), .. }]));
} }
// req: examples/001 req: form/002 req: codegen/003 // req: examples/001 req: form/002 req: codegen/003
#[test] #[test]
fn techdemo_exports_form_and_interaction_handles() { fn techdemo_exports_form_and_interaction_handles() {
assert_ne!( assert_ne!(handles::launch_work.id(), card_handles::advance_work.id());
ui::control_center::handles::launch_work.id(),
ui::issue_card::handles::advance_work.id()
);
assert_eq!( assert_eq!(
ui::control_center::forms::launch_work.field("title").resource, forms::launch_work.field("title").resource,
ui::control_center::forms::launch_work.id() forms::launch_work.id()
); );
} }
// req: codegen/006 // req: codegen/006
#[test] #[test]
fn techdemo_exports_generated_event_constants() { fn techdemo_exports_generated_event_constants() {
assert_eq!(ui::issue_lane::events::drop.as_str(), "drop"); assert_eq!(lane_events::drop.as_str(), "drop");
assert!(matches!( assert!(matches!(
slhx::event(ui::issue_lane::events::drop, "card-1"), slhx::event(lane_events::drop, "card-1"),
Effect::Emit { name, payload } if name == "drop" && payload == "card-1" Effect::Emit { name, payload } if name == "drop" && payload == "card-1"
)); ));
} }
+29 -26
View File
@@ -10,6 +10,9 @@ use slhx_axum::{
PageRequest, PageRequest,
}; };
use slhx_techdemo::ui; use slhx_techdemo::ui;
use slhx_techdemo::ui::control_center::{classes, forms, handles, slots};
use slhx_techdemo::ui::issue_card::handles as card_handles;
use slhx_techdemo::ui::issue_lane::handles as lane_handles;
use std::collections::{BTreeMap, VecDeque}; use std::collections::{BTreeMap, VecDeque};
use std::convert::Infallible; use std::convert::Infallible;
use std::net::SocketAddr; use std::net::SocketAddr;
@@ -266,13 +269,13 @@ async fn interact(
// req: push/001 req: push/003 req: examples/001 // req: push/001 req: push/003 req: examples/001
async fn events(Query(params): Query<BTreeMap<String, String>>) -> impl IntoResponse { async fn events(Query(params): Query<BTreeMap<String, String>>) -> impl IntoResponse {
if params.contains_key("once") { if params.contains_key("once") {
let effect = ui::control_center::slots::live_feed.render(&LiveFeed { tick: 1 }); let effect = slots::live_feed.render(&LiveFeed { tick: 1 });
return sse(stream::iter([Ok::<_, Infallible>(effect.into_batch(ui::BUILD_FINGERPRINT))]).boxed()); return sse(stream::iter([Ok::<_, Infallible>(effect.into_batch(ui::BUILD_FINGERPRINT))]).boxed());
} }
let batches = stream::unfold(1_u64, |tick| async move { let batches = stream::unfold(1_u64, |tick| async move {
tokio::time::sleep(Duration::from_secs(4)).await; tokio::time::sleep(Duration::from_secs(4)).await;
let effect = ui::control_center::slots::live_feed.render(&LiveFeed { tick }); let effect = slots::live_feed.render(&LiveFeed { tick });
Some((Ok::<_, Infallible>(effect.into_batch(ui::BUILD_FINGERPRINT)), tick + 1)) Some((Ok::<_, Infallible>(effect.into_batch(ui::BUILD_FINGERPRINT)), tick + 1))
}) })
.boxed(); .boxed();
@@ -281,7 +284,7 @@ async fn events(Query(params): Query<BTreeMap<String, String>>) -> impl IntoResp
fn registry(shared: Arc<Shared>) -> HandlerRegistry { fn registry(shared: Arc<Shared>) -> HandlerRegistry {
HandlerRegistry::new(ui::BUILD_FINGERPRINT) HandlerRegistry::new(ui::BUILD_FINGERPRINT)
.register_handle(ui::control_center::handles::launch_work, { .register_handle(handles::launch_work, {
let shared = shared.clone(); let shared = shared.clone();
move |form| { move |form| {
// req: form/002 req: examples/001 // req: form/002 req: examples/001
@@ -300,7 +303,7 @@ fn registry(shared: Arc<Shared>) -> HandlerRegistry {
demo_effects(&demo, "Launch accepted · 4 targets updated") demo_effects(&demo, "Launch accepted · 4 targets updated")
} }
}) })
.register_handle(ui::issue_card::handles::advance_work, { .register_handle(card_handles::advance_work, {
let shared = shared.clone(); let shared = shared.clone();
move |form| { move |form| {
// req: list/003 req: examples/001 // req: list/003 req: examples/001
@@ -318,7 +321,7 @@ fn registry(shared: Arc<Shared>) -> HandlerRegistry {
demo_effects(&demo, "Pipeline advanced") demo_effects(&demo, "Pipeline advanced")
} }
}) })
.register_handle(ui::issue_lane::handles::move_to_lane, { .register_handle(lane_handles::move_to_lane, {
let shared = shared.clone(); let shared = shared.clone();
move |form| { move |form| {
// req: list/003 req: examples/001 // req: list/003 req: examples/001
@@ -340,7 +343,7 @@ fn registry(shared: Arc<Shared>) -> HandlerRegistry {
demo_effects(&demo, "Drag-and-drop move persisted") demo_effects(&demo, "Drag-and-drop move persisted")
} }
}) })
.register_handle(ui::issue_card::handles::delete_work, { .register_handle(card_handles::delete_work, {
let shared = shared.clone(); let shared = shared.clone();
move |form| { move |form| {
// req: list/003 req: examples/001 // req: list/003 req: examples/001
@@ -359,7 +362,7 @@ fn registry(shared: Arc<Shared>) -> HandlerRegistry {
demo_effects(&demo, "Card removed") demo_effects(&demo, "Card removed")
} }
}) })
.register_handle(ui::issue_card::handles::spotlight_work, { .register_handle(card_handles::spotlight_work, {
let shared = shared.clone(); let shared = shared.clone();
move |form| { move |form| {
// req: examples/001 // req: examples/001
@@ -374,23 +377,23 @@ fn registry(shared: Arc<Shared>) -> HandlerRegistry {
demo_effects(&demo, "Inspector focused") demo_effects(&demo, "Inspector focused")
} }
}) })
.register_handle(ui::control_center::handles::simulate_push, { .register_handle(handles::simulate_push, {
let shared = shared.clone(); let shared = shared.clone();
move |_| { move |_| {
// req: push/003 req: examples/001 // req: push/003 req: examples/001
let mut demo = shared.demo.lock().unwrap(); let mut demo = shared.demo.lock().unwrap();
demo.log("Simulated push event produced the same EffectBatch shape"); demo.log("Simulated push event produced the same EffectBatch shape");
( (
ui::control_center::slots::live_feed.render(&LiveFeed { slots::live_feed.render(&LiveFeed {
tick: demo.activity.len() as u64, tick: demo.activity.len() as u64,
}), }),
ui::control_center::slots::activity.render(&activity_view(&demo)), slots::activity.render(&activity_view(&demo)),
ui::control_center::slots::notice.text("Push simulated · no client app code"), slots::notice.text("Push simulated · no client app code"),
slhx::event("slhx:island-orbit", island_snapshot(&demo)), slhx::event("slhx:island-orbit", island_snapshot(&demo)),
) )
} }
}) })
.register_handle(ui::control_center::handles::reset_demo, { .register_handle(handles::reset_demo, {
let shared = shared.clone(); let shared = shared.clone();
move |_| { move |_| {
// req: examples/001 // req: examples/001
@@ -411,12 +414,12 @@ fn update_work(demo: &mut DemoState, id: Option<&str>, update: impl FnOnce(&mut
fn demo_effects(demo: &DemoState, notice: &'static str) -> impl IntoEffect { fn demo_effects(demo: &DemoState, notice: &'static str) -> impl IntoEffect {
( (
ui::control_center::slots::hero_metrics.render(&hero_view(demo)), slots::hero_metrics.render(&hero_view(demo)),
ui::control_center::slots::board.html(render_board(demo)), slots::board.html(render_board(demo)),
ui::control_center::slots::activity.render(&activity_view(demo)), slots::activity.render(&activity_view(demo)),
ui::control_center::slots::inspector.render(&inspector_view(demo)), slots::inspector.render(&inspector_view(demo)),
ui::control_center::slots::notice.text(notice), slots::notice.text(notice),
ui::control_center::forms::launch_work.clear("title"), forms::launch_work.clear("title"),
slhx::event("slhx:island-orbit", island_snapshot(demo)), slhx::event("slhx:island-orbit", island_snapshot(demo)),
) )
} }
@@ -485,7 +488,7 @@ fn board_view(demo: &DemoState) -> BoardLanes {
.iter() .iter()
.enumerate() .enumerate()
.map(|(idx, (lane_id, title, description))| IssueLane { .map(|(idx, (lane_id, title, description))| IssueLane {
class: ui::control_center::classes::lane, class: classes::lane,
lane_id, lane_id,
title, title,
description, description,
@@ -510,11 +513,11 @@ fn issue_card(item: &WorkItem, selected: bool) -> IssueCard {
IssueCard { IssueCard {
class: if selected { class: if selected {
CssClasses::from([ CssClasses::from([
ui::control_center::classes::work_card, classes::work_card,
ui::control_center::classes::is_selected, classes::is_selected,
]) ])
} else { } else {
CssClasses::from(ui::control_center::classes::work_card) CssClasses::from(classes::work_card)
}, },
id: item.id, id: item.id,
title: item.title.clone(), title: item.title.clone(),
@@ -584,7 +587,7 @@ fn architecture_activity() -> SafeHtml {
fn render_control_center(page: ControlCenter) -> String { fn render_control_center(page: ControlCenter) -> String {
// req: html_safety/002 req: view/001 req: component/003 // req: html_safety/002 req: view/001 req: component/003
ui::control_center::render_html(&page).into_string() ui::render_html(&page).into_string()
} }
fn render_template(template: &impl Hemplate) -> String { fn render_template(template: &impl Hemplate) -> String {
@@ -666,9 +669,9 @@ mod tests {
#[test] #[test]
fn generated_css_class_flows_through_hemplate_dynamic_attribute() { fn generated_css_class_flows_through_hemplate_dynamic_attribute() {
let board = render_board(&DemoState::default()); let board = render_board(&DemoState::default());
assert_eq!(ui::control_center::classes::lane.as_str(), "lane"); assert_eq!(classes::lane.as_str(), "lane");
assert_eq!(ui::control_center::classes::work_card.as_str(), "work-card"); assert_eq!(classes::work_card.as_str(), "work-card");
assert_eq!(ui::control_center::classes::is_selected.as_str(), "is-selected"); assert_eq!(classes::is_selected.as_str(), "is-selected");
let document = Html::parse_fragment(board.as_str()); let document = Html::parse_fragment(board.as_str());
assert_eq!(document.select(&selector(".lanes > section.lane")).count(), 3); assert_eq!(document.select(&selector(".lanes > section.lane")).count(), 3);