100% Dioxus SPA: migrate all admin UIs to dioxus-bootstrap-css native islands #104

Open
opened 2026-03-28 17:27:54 +00:00 by mik-tf · 0 comments
Owner

Vision

Make Hero OS a true 100% Dioxus WASM SPA. Every page — shell, islands, and admin dashboards — renders as a native Dioxus component using dioxus-bootstrap-css. No iframes, no Askama, no server-rendered HTML in the user experience. This unlocks mobile (iOS/Android), desktop, and PWA from the same codebase.

Current state

  • Shell (hero_os_app): Dioxus RSX with 203 inline styles → migrate to dioxus-bootstrap
  • 73 islands (hero_archipelagos): Dioxus RSX with 1,886 inline styles → migrate to dioxus-bootstrap
  • 14 admin UIs: Askama HTML + Bootstrap CSS + JS, served via iframe → replace with native Dioxus _app crates

All 14 admin UIs already use Bootstrap 5.3 HTML. dioxus-bootstrap-css provides a 1:1 mapping from Bootstrap HTML to Dioxus RSX components. The translation is structural, not architectural.

Prior art

A previous migration attempt (backup: 2026-03-19) created 17 _ui_wasm Dioxus crates for admin services. The code quality was good and the pattern worked (dual-mode: Dioxus WASM or Askama fallback). It was shelved to prioritize feature work. The backup serves as reference for component patterns but is now stale (features shipped since: JWT auth, TTS trackbar, voice pipeline, conversations, etc.).

Architecture: additive, zero-risk

Key principle: we never modify existing _ui crates. The migration is 100% additive.

hero_redis/crates/
├── hero_redis_server/     ← unchanged (RPC backend)
├── hero_redis_sdk/        ← unchanged (generated client)
├── hero_redis_ui/         ← unchanged (Askama HTTP, keeps working)
└── hero_redis_app/        ← NEW: Dioxus WASM island
    ├── Cargo.toml         (depends on hero_redis_sdk + dioxus-bootstrap-css)
    └── src/island.rs      (calls SDK, renders with Card/Table/Button)

In hero_os_app, feature-gated switch:

#[cfg(feature = "island-redis-native")]
"redis" => rsx! { hero_redis_app::island::RedisIsland {} }
#[cfg(not(feature = "island-redis-native"))]
"redis" => rsx! { ExternalServiceIframe { src: "/hero_redis_ui/" } }
  • Default: feature off = iframe (current behavior, nothing changes)
  • Enable feature = native Dioxus island
  • Toggle per service, merge to development at any time
  • make dist, make pack, make smoke — all work exactly as today
  • Askama UI stays as standalone direct-access fallback

14 admin UIs — migration complexity

# Service Templates JS files Complexity Notes
1 hero_auth 9 0 Simple Forms, user table, stats
2 hero_redis 12 1 Simple Key browser, stats dashboard
3 hero_voice 0 1 Simple Health + config display
4 hero_browser_mcp 2 3 Simple Status + session list
5 hero_proxy 0 5 Simple Route table, stats
6 hero_aibroker 7 1 Simple Model list, chat test, stats
7 hero_embedder 9 3 Simple Model list, search, stats
8 hero_inspector 4 4 Medium Service discovery, method browser
9 hero_collab 4 7 Medium Workspace/channel/message views
10 hero_books 16 1 Medium Library browser, book viewer
11 hero_foundry 15 3 Medium Git repo browser, file viewer
12 hero_compute 9 5 Medium VM management, node stats
13 hero_proc 2 25 Hard Live log streaming, process tree
14 hero_whiteboard 6 32 Hard Konva.js canvas (may stay iframe)

Simple (6): Standard CRUD — Card, Table, Form, Button. Mechanical translation. ~1 session each.
Medium (5): More complex layouts but still standard Bootstrap patterns. ~1-2 sessions each.
Hard (2-3): Complex JS logic (canvas, terminal, live streaming). May need web-sys bindings or remain as special-case iframes.

Phase plan

Phase 0: Foundation

  • Add dioxus-bootstrap-css = "0.3" to hero_os_app
  • Add BootstrapHead + ThemeProvider to shell root
  • Migrate shell components (toolbar, dock, window) from inline styles to dioxus-bootstrap
  • Migrate hero_archipelagos_core shared components
  • Establish the _app crate pattern with one simple service (hero_auth)

Phase 1: Simple services (6 services)

  • hero_auth_app
  • hero_redis_app
  • hero_voice_app
  • hero_browser_app
  • hero_proxy_app
  • hero_aibroker_app

Phase 2: Medium services (5 services)

  • hero_embedder_app
  • hero_inspector_app
  • hero_collab_app
  • hero_books_app
  • hero_foundry_app

Phase 3: Complex services + compute

  • hero_compute_app
  • hero_proc_app
  • hero_whiteboard_app (assess: native Dioxus canvas or keep iframe)

Phase 4: Shell + islands polish

  • Replace remaining 203 inline styles in hero_os_app
  • Replace remaining 1,886 inline styles in archipelagos
  • Remove hero-bootstrap-bridge.css
  • Unify ThemeProvider as single source of truth

Phase 5: Multi-platform

  • dx build --platform desktop works
  • dx build --platform ios works
  • dx build --platform android works
  • PWA manifest + service worker

dioxus-bootstrap-css component mapping

Bootstrap HTML → dioxus-bootstrap-css RSX (1:1):

<div class="card">              →  Card { body: rsx! { ... } }
<button class="btn btn-primary"> →  Button { color: Color::Primary }
<table class="table table-hover">→  Table { hover: true }
<div class="modal">              →  Modal { show: signal, ... }
<div class="dropdown">           →  Dropdown { open: signal, ... }
<ul class="nav nav-tabs">        →  TabList { active: signal, tabs: vec![...] }
<div class="container">          →  Container { children }
<div class="row">                →  Row { children }
<div class="col-md-6">           →  Col { md: ColumnSize::Span(6) }

50+ components available. Zero JavaScript — all interactivity via Dioxus signals.

What this enables

  • True SPA: Single WASM binary, no iframes, no server-rendered HTML
  • Multi-platform: Same Rust code → web, desktop, iOS, Android
  • Type-safe UI: Color::Primary not "btn-primry" typos
  • Unified theming: One ThemeProvider controls everything
  • Offline-capable: CSS + icons bundled in WASM
  • Faster navigation: No iframe load per admin page
  • Testable: Playwright can reach all elements (no iframe crossing)

Rules

  1. Never modify _ui crates — additive only
  2. Feature-gate everything — default off, iframe fallback
  3. One service at a time — test before next
  4. Pin dioxus-bootstrap-css version — same version everywhere
  5. Use backup as reference — patterns from 2026-03-19 backup, not copy-paste (stale code)
  6. Playwright test per migration — verify visual parity before flipping flag

Signed-off-by: mik-tf

## Vision Make Hero OS a true 100% Dioxus WASM SPA. Every page — shell, islands, and admin dashboards — renders as a native Dioxus component using dioxus-bootstrap-css. No iframes, no Askama, no server-rendered HTML in the user experience. This unlocks mobile (iOS/Android), desktop, and PWA from the same codebase. ## Current state - **Shell (hero_os_app):** Dioxus RSX with 203 inline styles → migrate to dioxus-bootstrap - **73 islands (hero_archipelagos):** Dioxus RSX with 1,886 inline styles → migrate to dioxus-bootstrap - **14 admin UIs:** Askama HTML + Bootstrap CSS + JS, served via iframe → replace with native Dioxus `_app` crates All 14 admin UIs already use Bootstrap 5.3 HTML. dioxus-bootstrap-css provides a 1:1 mapping from Bootstrap HTML to Dioxus RSX components. The translation is structural, not architectural. ## Prior art A previous migration attempt (backup: 2026-03-19) created 17 `_ui_wasm` Dioxus crates for admin services. The code quality was good and the pattern worked (dual-mode: Dioxus WASM or Askama fallback). It was shelved to prioritize feature work. The backup serves as reference for component patterns but is now stale (features shipped since: JWT auth, TTS trackbar, voice pipeline, conversations, etc.). ## Architecture: additive, zero-risk **Key principle: we never modify existing `_ui` crates.** The migration is 100% additive. ``` hero_redis/crates/ ├── hero_redis_server/ ← unchanged (RPC backend) ├── hero_redis_sdk/ ← unchanged (generated client) ├── hero_redis_ui/ ← unchanged (Askama HTTP, keeps working) └── hero_redis_app/ ← NEW: Dioxus WASM island ├── Cargo.toml (depends on hero_redis_sdk + dioxus-bootstrap-css) └── src/island.rs (calls SDK, renders with Card/Table/Button) ``` **In hero_os_app, feature-gated switch:** ```rust #[cfg(feature = "island-redis-native")] "redis" => rsx! { hero_redis_app::island::RedisIsland {} } #[cfg(not(feature = "island-redis-native"))] "redis" => rsx! { ExternalServiceIframe { src: "/hero_redis_ui/" } } ``` - Default: feature off = iframe (current behavior, nothing changes) - Enable feature = native Dioxus island - Toggle per service, merge to development at any time - `make dist`, `make pack`, `make smoke` — all work exactly as today - Askama UI stays as standalone direct-access fallback ## 14 admin UIs — migration complexity | # | Service | Templates | JS files | Complexity | Notes | |---|---------|-----------|----------|------------|-------| | 1 | hero_auth | 9 | 0 | Simple | Forms, user table, stats | | 2 | hero_redis | 12 | 1 | Simple | Key browser, stats dashboard | | 3 | hero_voice | 0 | 1 | Simple | Health + config display | | 4 | hero_browser_mcp | 2 | 3 | Simple | Status + session list | | 5 | hero_proxy | 0 | 5 | Simple | Route table, stats | | 6 | hero_aibroker | 7 | 1 | Simple | Model list, chat test, stats | | 7 | hero_embedder | 9 | 3 | Simple | Model list, search, stats | | 8 | hero_inspector | 4 | 4 | Medium | Service discovery, method browser | | 9 | hero_collab | 4 | 7 | Medium | Workspace/channel/message views | | 10 | hero_books | 16 | 1 | Medium | Library browser, book viewer | | 11 | hero_foundry | 15 | 3 | Medium | Git repo browser, file viewer | | 12 | hero_compute | 9 | 5 | Medium | VM management, node stats | | 13 | hero_proc | 2 | 25 | Hard | Live log streaming, process tree | | 14 | hero_whiteboard | 6 | 32 | Hard | Konva.js canvas (may stay iframe) | **Simple (6):** Standard CRUD — Card, Table, Form, Button. Mechanical translation. ~1 session each. **Medium (5):** More complex layouts but still standard Bootstrap patterns. ~1-2 sessions each. **Hard (2-3):** Complex JS logic (canvas, terminal, live streaming). May need web-sys bindings or remain as special-case iframes. ## Phase plan ### Phase 0: Foundation - [ ] Add `dioxus-bootstrap-css = "0.3"` to hero_os_app - [ ] Add `BootstrapHead` + `ThemeProvider` to shell root - [ ] Migrate shell components (toolbar, dock, window) from inline styles to dioxus-bootstrap - [ ] Migrate hero_archipelagos_core shared components - [ ] Establish the `_app` crate pattern with one simple service (hero_auth) ### Phase 1: Simple services (6 services) - [ ] hero_auth_app - [ ] hero_redis_app - [ ] hero_voice_app - [ ] hero_browser_app - [ ] hero_proxy_app - [ ] hero_aibroker_app ### Phase 2: Medium services (5 services) - [ ] hero_embedder_app - [ ] hero_inspector_app - [ ] hero_collab_app - [ ] hero_books_app - [ ] hero_foundry_app ### Phase 3: Complex services + compute - [ ] hero_compute_app - [ ] hero_proc_app - [ ] hero_whiteboard_app (assess: native Dioxus canvas or keep iframe) ### Phase 4: Shell + islands polish - [ ] Replace remaining 203 inline styles in hero_os_app - [ ] Replace remaining 1,886 inline styles in archipelagos - [ ] Remove hero-bootstrap-bridge.css - [ ] Unify ThemeProvider as single source of truth ### Phase 5: Multi-platform - [ ] `dx build --platform desktop` works - [ ] `dx build --platform ios` works - [ ] `dx build --platform android` works - [ ] PWA manifest + service worker ## dioxus-bootstrap-css component mapping Bootstrap HTML → dioxus-bootstrap-css RSX (1:1): ``` <div class="card"> → Card { body: rsx! { ... } } <button class="btn btn-primary"> → Button { color: Color::Primary } <table class="table table-hover">→ Table { hover: true } <div class="modal"> → Modal { show: signal, ... } <div class="dropdown"> → Dropdown { open: signal, ... } <ul class="nav nav-tabs"> → TabList { active: signal, tabs: vec![...] } <div class="container"> → Container { children } <div class="row"> → Row { children } <div class="col-md-6"> → Col { md: ColumnSize::Span(6) } ``` 50+ components available. Zero JavaScript — all interactivity via Dioxus signals. ## What this enables - **True SPA:** Single WASM binary, no iframes, no server-rendered HTML - **Multi-platform:** Same Rust code → web, desktop, iOS, Android - **Type-safe UI:** `Color::Primary` not `"btn-primry"` typos - **Unified theming:** One `ThemeProvider` controls everything - **Offline-capable:** CSS + icons bundled in WASM - **Faster navigation:** No iframe load per admin page - **Testable:** Playwright can reach all elements (no iframe crossing) ## Rules 1. **Never modify `_ui` crates** — additive only 2. **Feature-gate everything** — default off, iframe fallback 3. **One service at a time** — test before next 4. **Pin dioxus-bootstrap-css version** — same version everywhere 5. **Use backup as reference** — patterns from 2026-03-19 backup, not copy-paste (stale code) 6. **Playwright test per migration** — verify visual parity before flipping flag Signed-off-by: mik-tf
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/home#104
No description provided.