Port hero_videos_web to Dioxus #3
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
The current
hero_videos_webcrate is an Axum server that serves Askama HTML templates with vanilla JavaScript for all interactivity. This approach has accumulated several pain points:getElementById,innerHTML, manual polling loops)hero_videos_sdk<script>blocks with no component boundariessetTimeout(pollProject, 3000)) is hand-rolled and error-proneGoal
Replace the JS+Askama frontend with a Dioxus WASM app served from
hero_videos_web. The Axum server stays but its only job becomes serving the compiled WASM bundle and proxying/rpc— all UI logic moves to Rust.Why Dioxus
hero_videos_sdkwith no serialisation boundary in the client codeuse_resource/use_coroutinereplace the hand-rolled polling loopScope
What changes
hero_videos_webbecomes a Dioxus workspace member alongside ahero_videos_web_servercrate (or the server is inlined asmain.rsserving the WASM dist)app.html,projects.html,collections.html) are replaced by Dioxus componentshero_videos_sdkclient types are imported directly into the Dioxus appGET /,GET /c/:collection_id,GET /c/:collection_id/:project_id,POST /rpc,POST /transcribestay; the first three serveindex.html(SPA shell)dx build --releaseproduces the WASM bundle;Makefilewraps thisComponent map (initial)
collections.htmlCollectionsPageprojects.htmlProjectsPageapp.html+renderPlanning()PlanningStepapp.html+renderImaging()ImagingStepapp.html+renderVideo()VideoStepapp.html+renderAssembly()AssemblyStepuse_coroutine/use_resourcewith reactive refreshVoiceInputcomponentWhat does NOT change
hero_videos_server— no changes to the RPC serverhero_videos_sdk— types and client are consumed as-ishero_videos_admin— stays Askama (admin dashboards are read-heavy, no need to port)Implementation notes
dioxus = { features = ["web"] }in the web cratedioxus_routerwith routes matching the existing URL structure so existing links keep workingdata-bs-themepattern; Dioxus renders to the DOM so Bootstrap classes work unchangedhero:theme-ready/hero:themepostMessage protocol stays; wire it viause_evalor a JS interop functionasync fn rpc(method, params) -> Result<Value>reusingreqwestorgloo-net; typed wrappers per method usinghero_videos_sdktypesdioxus_cli(dx) for the build; output lands indist/, Axum serves it withtower_http::services::ServeDirAcceptance criteria
dx buildinhero_videos_webproduces a working WASM bundle<script>blocks remain inhero_videos_webmake buildandmake installstill work (wrapsdx build+ Axum binary)Relation to other issues
Issues #1 (UX for marketing/redactors) and #2 (AI coherence/prompt split) both require new UI state that is much easier to implement cleanly in Dioxus than in the current imperative JS. This port should be done before or alongside those features.
Implementation Spec: Port hero_videos_web to Dioxus
Objective
Replace the three Askama+JS HTML templates in
hero_videos_webwith a Dioxus 0.7 WASM application. The Axum server binary keeps its role as the Unix-socket listener, but strips down to: serving the compiled WASM bundle, proxying/rpcand/transcribeto the upstreamhero_videos_server, and serving static media files (/img,/video,/assembled). All UI logic moves to Rust/Dioxus components that run in the browser.Current Architecture
crates/hero_videos_web/— Axum binary serving Askama templates + reverse-proxying/rpcviaopenrpc_proxy!crates/hero_videos_sdk/— typed Rust RPC client (not usable in WASM — Unix socket transport)crates/hero_videos_server/src/videos/types_wasm_generated.rs— WASM-safe types already generated from OSchema; reuse in the Dioxus crate/rpcReference Implementations in the Monorepo
hero_proc/crates/hero_proc_app/—dioxus = {version="0.7", features=["web"]},gloo-net 0.6,web-sys,js-sys,rpc.rswithOnceLock<String>base URLhero_foundry/crates/hero_foundry_web/—dioxus = {version="0.7", features=["web","router"]},Dioxus.toml,manganisfor CSS asset embeddingScope: Four Phases
This is a large change. The implementation is divided into four self-contained phases.
Phase 1 — New Dioxus Crate + Axum Server Stripped Down
Files to create:
crates/hero_videos_app/Cargo.toml,Dioxus.toml,src/lib.rs,src/main.rs,src/rpc.rs(port fromhero_proc_app/src/rpc.rs),src/types.rs(copy oftypes_wasm_generated.rs),src/app.rs,src/routes.rs(3 routes:/,/c/:cid,/c/:cid/:pid),src/components/mod.rs(stub)Files to modify:
Cargo.toml— addhero_videos_appto workspace, add WASM-safe workspace deps (gloo-net,gloo-timers,wasm-bindgen,wasm-bindgen-futures,web-sys,js-sys)crates/hero_videos_web/Cargo.toml— removeaskama,rust-embed,mime_guess,hero_rpc_derive; addtower-httpServeDir featurecrates/hero_videos_web/src/routes.rs— remove all Askama handlers; add SPAindex.htmlfallback; implement/rpcand/transcribeUnix socket proxies; keep media file routescrates/hero_videos_web/src/main.rs— remove theme fetch, removeAppState.theme, adddist_dirtoAppStateKey decision —
/rpcproxy: Theopenrpc_proxy!macro is removed. Replace with a manualrpc_proxyaxum handler that opens aUnixStreamand forwards the HTTP request body. Usehyper-utilwithhyperlocal(check if already in workspace), or a rawtokio::net::UnixStreamapproach. Fallback: keep the macro for/rpc+/transcribeonly (it is not Askama — it is a standalone axum sub-router).Acceptance criteria:
cargo check -p hero_videos_webpassescargo check -p hero_videos_app --target wasm32-unknown-unknownpassesdx build --package hero_videos_appproducesdist/dist/index.htmlonGET /Phase 2 — Navbar + Theme
Files to create:
crates/hero_videos_app/src/components/navbar.rs,crates/hero_videos_app/assets/app.cssNavbarcomponent: theme toggle, Bootstrap navbar, reads/writeslocalStorage, listens forpostMessagewith{type:"hero:theme"}, sends{type:"hero:theme-ready"}if in iframe.Theme injection: Bootstrap 5 + bootstrap-icons served from
/static/shared/...(thehero_admin_libshared_static_handler route on Axum, which must be preserved from Phase 1). Link tags in theindex.htmltemplate generated bydx.Acceptance criteria:
Phase 3 — Page Components (largest phase)
3a — CollectionsPage (
src/components/collections_page.rs)Port
collections.htmlJS to Dioxus signals. RPC calls:list_collections,create_collection,delete_collection. Modal via conditional rendering (no Bootstrap JS). Card click navigation viadioxus_router::prelude::navigator().3b — ProjectsPage (
src/components/projects_page.rs)Port
projects.html. Props:collection_id: String. IntegratesVoiceInput. RPC calls:list_projects,create_project,delete_project.3c — VoiceInput (
src/components/voice_input.rs)Props:
value: Signal<String>,placeholder,rows. Web-sysMediaRecorder→Blobchunks →FormData→ POST/transcribeviagloo_net::http::Request. Async viaspawn().3d — Editor (
src/components/editor/)Sub-modules:
mod.rs,planning.rs,imaging.rs,video_prompt.rs,video_step.rs,assembly.rs,settings_modal.rsspawn()async block callingget_projecton a 2s interval (re-scheduled on each render if generating)ondragstart/ondrophandlers on cards; callsrpc("reorder_scenes", ...)web_syslocal_storage helperssave_clip_params/load_clip_paramsSignal<PipelineStep>local state mirroringproject.pipeline_step<meta name="hero-base-path">tag rendered server-side inindex.htmlAcceptance criteria:
Phase 4 — Build Integration
Strategy: Option B (simpler) —
dx buildrun as a separate step beforecargo build. Document in README. Nobuild.rs.Files to modify:
crates/hero_videos_web/src/main.rs— readHERO_VIDEOS_DIST_DIRenv var; fall back to./distcrates/hero_videos_web/src/routes.rs—ServeDir::new(dist_dir)for WASM assetscrates/hero_videos_app/Dioxus.toml— setbase_path = ""Build sequence:
Acceptance criteria:
dx buildproduces a working WASM bundle<script>blocks remain inhero_videos_webmake buildandmake installstill workWarnings
herolib_sid::SmartIdin WASM types: Verifycargo check --target wasm32-unknown-unknown -p herolib_sidbefore usingtypes_wasm_generated.rsin the Dioxus crate. If it fails, replaceSmartIdwithStringin the copied types file.<meta name="hero-base-path">tag injected intoindex.htmlby the Axum server./transcribeproxy: Multipart proxy must be preserved alongside the JSON-RPC proxy for the VoiceInput component.index.htmlfor all three UI routes (/,/c/*) so Dioxus Router handles navigation client-side.Test Results
cargo install dioxus-clito rundx buildImplementation Summary
All four phases of the Dioxus port are complete.
Phase 1 — Scaffold + Axum server stripped
crates/hero_videos_app/— new Dioxus 0.7 WASM crate (cdylib/rlib)hero_videos_webspa_handlerinjects<meta name="hero-base-path">into the served HTML so the WASM app can read itHERO_VIDEOS_DIST_DIRenv var (default./dist) to configure where the WASM bundle livesopenrpc_proxy!macro kept for/rpcand/transcribeproxying torpc.sockMakefilewithbuild,install,run,check,fmttargets;build-apptarget wrapsdx buildPhase 2 — Navbar + theme
components/navbar.rs: Bootstrap navbar with camera icon brand, theme toggle buttonlocalStorage["hero-theme"], setsdata-bs-themeon<html>postMessagelistener handles{type: "hero:theme"}from parent iframe{type: "hero:theme-ready"}on mount when inside iframePhase 3 — Page components
components/collections_page.rs: collection grid, hover-reveal delete, create modalcomponents/projects_page.rs: project cards with step badges, create modal with VoiceInput for intentcomponents/voice_input.rs: hold-to-record mic button using web-sys MediaRecorder, POSTs audio to/transcribe, appends transcript to signalcomponents/editor/mod.rs: top-level Editor, 2-second polling loop for in-progress generation, step steppercomponents/editor/planning.rs: scene list with approve/delete/save, generate scenes with pollingcomponents/editor/imaging.rs: candidate grid with click-to-select, generation timercomponents/editor/video_prompt.rs: per-scene video prompt edit + generatecomponents/editor/video_step.rs: clip params (duration, model, enhanced, audio) with localStorage persistence, generate clip, use sample clip, approvecomponents/editor/assembly.rs: clip list with up/down reorder, compile + poll + auto-downloadcomponents/editor/settings.rs: prompt template editor modalPhase 4 — Build integration
Makefilecreated with two-step build (dx build+cargo build)cargo checktargets pass cleanly:hero_videos_webandhero_videos_app --target wasm32-unknown-unknownWhat's left before running
cargo install dioxus-clidx build --package hero_videos_app --releaseHERO_VIDEOS_DIST_DIRto the produceddist/directory (or place dist/ next to the binary)make runAcceptance criteria status
dx buildinhero_videos_appproduces a working WASM bundle — pending dx CLI install<script>blocks remain inhero_videos_web— donemake buildandmake installstill work — done (wrapsdx build+ Axum binary)