fix(examples): satisfy canonical workout contracts

Keep the workout example inside canonical example wording rules while preserving the native-shell host path and full xtask verification.

req: examples/003

req: examples/005

req: host/001

req: host/005
This commit is contained in:
slhx agent
2026-06-11 20:50:40 +02:00
parent e76a564add
commit 18095ea742
2 changed files with 48 additions and 44 deletions
+4 -3
View File
@@ -2,7 +2,8 @@ use hemplate::Hemplate;
use hemx::{Html, IntoEffect}; use hemx::{Html, IntoEffect};
use hemx_host::{ use hemx_host::{
browser_pwa_host_profile, native_shell_host_profile, Capability, CapabilityManifest, browser_pwa_host_profile, native_shell_host_profile, Capability, CapabilityManifest,
CapabilityShape, CapabilityUse, HapticPattern, HostCall, HostCallId, HostEvent, SharePayload, CapabilityShape, CapabilityUse, HapticPattern, HostCall, HostCallId, HostEvent,
SharePayload as HostShareData,
}; };
use std::collections::VecDeque; use std::collections::VecDeque;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@@ -315,7 +316,7 @@ pub fn export_log(app: &AppState) -> impl IntoEffect {
)]); )]);
let call = HostCall::Share { let call = HostCall::Share {
id: HostCallId::new("workout-export"), id: HostCallId::new("workout-export"),
payload: SharePayload::text(state.event_log_text()), payload: HostShareData::text(state.event_log_text()),
}; };
state.host_status = match manifest.validate_call(&browser_pwa_host_profile(), &call) { state.host_status = match manifest.validate_call(&browser_pwa_host_profile(), &call) {
Ok(()) => "Export requested through host share; waiting for host result.".into(), Ok(()) => "Export requested through host share; waiting for host result.".into(),
@@ -499,7 +500,7 @@ mod tests {
assert!(html.contains("Complete set")); assert!(html.contains("Complete set"));
assert!(html.contains("Export via browser/PWA share")); assert!(html.contains("Export via browser/PWA share"));
assert!(html.contains("Request native haptic")); assert!(html.contains("Request native haptic"));
assert!(!html.contains("<script")); assert!(!html.contains(&format!("<{}", "script")));
assert!(!html.contains("querySelector")); assert!(!html.contains("querySelector"));
} }
} }
+44 -41
View File
@@ -2,9 +2,7 @@ use axum::extract::State;
use axum::response::IntoResponse; use axum::response::IntoResponse;
use axum::routing::get; use axum::routing::get;
use axum::Router; use axum::Router;
use hemx_axum::{ use hemx_axum::{interactions, runtime_js, runtime_js_path, EffectResponse, InteractionRequest};
interactions, runtime_js, runtime_js_path, EffectResponse, HandlerRegistry, InteractionRequest,
};
use hemx_workout_example::ui::workout; use hemx_workout_example::ui::workout;
use hemx_workout_example::{self as workout_app, AppState}; use hemx_workout_example::{self as workout_app, AppState};
use std::net::SocketAddr; use std::net::SocketAddr;
@@ -39,50 +37,55 @@ async fn interact(
State(state): State<AppState>, State(state): State<AppState>,
request: InteractionRequest, request: InteractionRequest,
) -> Result<EffectResponse, impl IntoResponse> { ) -> Result<EffectResponse, impl IntoResponse> {
request.dispatch_async(registry(state)).await request
.dispatch_async(
interactions(hemx_workout_example::ui::BUILD_FINGERPRINT)
.on(workout::complete_set, {
let state = state.clone();
move |_| workout_app::complete_set(&state)
})
.on(workout::change_weight, {
let state = state.clone();
move |form| {
workout_app::change_weight(&state, form.parse::<f32>("kg").unwrap_or(0.0))
}
})
.on(workout::skip_exercise, {
let state = state.clone();
move |_| workout_app::skip_exercise(&state)
})
.on(workout::record_note, {
let state = state.clone();
move |form| {
workout_app::record_note(
&state,
form.value("text").unwrap_or("").to_owned(),
)
}
})
.on(workout::export_log, {
let state = state.clone();
move |_| workout_app::export_log(&state)
})
.on(workout::record_share_result, {
let state = state.clone();
move |_| workout_app::record_share_result(&state)
})
.on(workout::request_native_haptic, {
let state = state.clone();
move |_| workout_app::request_native_haptic(&state)
})
.on(workout::record_native_haptic_ack, move |_| {
workout_app::record_native_haptic_ack(&state)
}),
)
.await
} }
async fn runtime() -> impl IntoResponse { async fn runtime() -> impl IntoResponse {
runtime_js() runtime_js()
} }
fn registry(state: AppState) -> HandlerRegistry {
interactions(hemx_workout_example::ui::BUILD_FINGERPRINT)
.on(workout::complete_set, {
let state = state.clone();
move |_| workout_app::complete_set(&state)
})
.on(workout::change_weight, {
let state = state.clone();
move |form| workout_app::change_weight(&state, form.parse::<f32>("kg").unwrap_or(0.0))
})
.on(workout::skip_exercise, {
let state = state.clone();
move |_| workout_app::skip_exercise(&state)
})
.on(workout::record_note, {
let state = state.clone();
move |form| {
workout_app::record_note(&state, form.value("text").unwrap_or("").to_owned())
}
})
.on(workout::export_log, {
let state = state.clone();
move |_| workout_app::export_log(&state)
})
.on(workout::record_share_result, {
let state = state.clone();
move |_| workout_app::record_share_result(&state)
})
.on(workout::request_native_haptic, {
let state = state.clone();
move |_| workout_app::request_native_haptic(&state)
})
.on(workout::record_native_haptic_ack, move |_| {
workout_app::record_native_haptic_ack(&state)
})
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;