Wire up URL routing so browser URLs open apps #30

Open
opened 2026-04-03 15:04:12 +00:00 by timur · 6 comments
Owner

Problem

routing.rs contains a full URL routing implementation (/space/:context/:apps format) with parsing, building, and History API integration — but it is never imported or used. The module is dead code.

This means:

  • Navigating to /hero_os_ui/space/geomind/contacts loads the app but ignores the URL — it shows whatever was in localStorage instead of opening the contacts app
  • Opening/closing windows never updates the browser URL bar
  • Browser back/forward buttons don't work for app navigation
  • URLs are not shareable — you can't send someone a link to a specific app

Root Cause

main.rs never declares mod routing; — the module is orphaned.

Expected Behavior

  1. Loading /hero_os_ui/space/geomind/contacts should open the contacts app in the geomind context
  2. Loading /hero_os_ui/space/work/contacts+filesystem should open both apps
  3. Opening/closing apps should update the URL bar
  4. Browser back/forward should navigate between app states
  5. Query params like ?contacts.id=abc should be passed to islands

Implementation Plan

  1. Add mod routing; to main.rs
  2. On mount (after auth), parse the current URL and open matching windows
  3. Add a use_effect that syncs window state → URL bar via replace_url()
  4. Add a popstate event listener for browser back/forward
  5. When URL-opened windows differ from localStorage state, URL wins

Secondary Issue

livekit.js in Dioxus.toml uses absolute path /livekit.js which 404s because base_path is hero_os_ui. Should reference it correctly.

## Problem `routing.rs` contains a full URL routing implementation (`/space/:context/:apps` format) with parsing, building, and History API integration — but it is **never imported or used**. The module is dead code. This means: - Navigating to `/hero_os_ui/space/geomind/contacts` loads the app but **ignores the URL** — it shows whatever was in localStorage instead of opening the contacts app - Opening/closing windows never updates the browser URL bar - Browser back/forward buttons don't work for app navigation - URLs are not shareable — you can't send someone a link to a specific app ## Root Cause `main.rs` never declares `mod routing;` — the module is orphaned. ## Expected Behavior 1. Loading `/hero_os_ui/space/geomind/contacts` should open the contacts app in the geomind context 2. Loading `/hero_os_ui/space/work/contacts+filesystem` should open both apps 3. Opening/closing apps should update the URL bar 4. Browser back/forward should navigate between app states 5. Query params like `?contacts.id=abc` should be passed to islands ## Implementation Plan 1. Add `mod routing;` to `main.rs` 2. On mount (after auth), parse the current URL and open matching windows 3. Add a `use_effect` that syncs window state → URL bar via `replace_url()` 4. Add a `popstate` event listener for browser back/forward 5. When URL-opened windows differ from localStorage state, URL wins ## Secondary Issue `livekit.js` in `Dioxus.toml` uses absolute path `/livekit.js` which 404s because base_path is `hero_os_ui`. Should reference it correctly.
Author
Owner

Implemented in commit eed6f4ebade687bdeef148d92296169f09be271c on the development branch.

Changes

  • main.rs: Added mod routing;, URL-based initial context, URL-to-windows restoration on mount, replace_url sync effect, popstate listener for back/forward
  • controller.rs: Added push_url_state() helper, called from open_window() and close_window() for discrete history entries
  • Cargo.toml: Added History and PopStateEvent web-sys features
  • Dioxus.toml: Fixed livekit.js path from /livekit.js to /hero_os_ui/livekit.js

How it works

URL Effect
/hero_os_ui/space/geomind Opens geomind context, restores from localStorage
/hero_os_ui/space/geomind/contacts Opens contacts app in geomind
/hero_os_ui/space/work/contacts+filesystem Opens both apps in work context

All 9 routing unit tests pass. WASM build succeeds.

Implemented in commit [eed6f4ebade687bdeef148d92296169f09be271c](https://forge.ourworld.tf/lhumina_code/hero_os/commit/eed6f4ebade687bdeef148d92296169f09be271c) on the `development` branch. ### Changes - **`main.rs`**: Added `mod routing;`, URL-based initial context, URL-to-windows restoration on mount, `replace_url` sync effect, `popstate` listener for back/forward - **`controller.rs`**: Added `push_url_state()` helper, called from `open_window()` and `close_window()` for discrete history entries - **`Cargo.toml`**: Added `History` and `PopStateEvent` web-sys features - **`Dioxus.toml`**: Fixed `livekit.js` path from `/livekit.js` to `/hero_os_ui/livekit.js` ### How it works | URL | Effect | |---|---| | `/hero_os_ui/space/geomind` | Opens geomind context, restores from localStorage | | `/hero_os_ui/space/geomind/contacts` | Opens contacts app in geomind | | `/hero_os_ui/space/work/contacts+filesystem` | Opens both apps in work context | All 9 routing unit tests pass. WASM build succeeds.
Author
Owner

Pushed to development: 25b1662176a35496dd6ec288f43712327a43a731 (rebased on latest remote).

Pushed to `development`: [25b1662176a35496dd6ec288f43712327a43a731](https://forge.ourworld.tf/lhumina_code/hero_os/commit/25b1662176a35496dd6ec288f43712327a43a731) (rebased on latest remote).
Author
Owner

Consolidated #21 (Hero OS URLs working) into this issue. The original vision from #21 — shareable URLs, per-app deep linking, context+app params in URL, archipelago integration — is all captured in the implementation plan above.

Next steps: verify current routing implementation works end-to-end, then use hero_browser_mcp for automated route testing.

Consolidated #21 (Hero OS URLs working) into this issue. The original vision from #21 — shareable URLs, per-app deep linking, context+app params in URL, archipelago integration — is all captured in the implementation plan above. Next steps: verify current routing implementation works end-to-end, then use hero_browser_mcp for automated route testing.
Author
Owner

Routing Verification Report

Verified the URL routing implementation on branch development (commit ee2cfe6).

Implementation Status

All 5 items from the implementation plan are complete:

# Requirement Status Location
1 mod routing; in main.rs Done hero_os_app/src/main.rs:27 (web-platform gated)
2 Parse URL on mount, URL context wins over localStorage Done main.rs:180-186 (initial_context) + main.rs:703-743 (window restoration)
3 use_effect syncs window state → URL via replace_url() Done main.rs:792-811
4 popstate listener for browser back/forward Done main.rs:813-877
5 URL wins over localStorage for app state Done URL-opened windows take priority

Unit Tests

All 9 routing unit tests pass (cargo test -p hero_os_app routing):

  • test_parse_context_only/space/default
  • test_parse_single_app/space/default/contacts
  • test_parse_multiple_apps/space/default/contacts+filesystem
  • test_parse_with_proxy_prefix/hero_os_ui/space/workspace-1/contacts
  • test_parse_query_params?contacts.id=abc123&contacts.view=detail
  • test_parse_root_returns_none
  • test_parse_no_space_prefix_returns_none
  • test_build_url_context_only
  • test_build_url_with_apps

Secondary Issue

livekit.js path fixed from /livekit.js to /hero_os_ui/livekit.js in Dioxus.toml

Architecture Summary

  • Two-tier URL updates: push_url() for discrete user actions (creates history entries) vs replace_url() for reactive sync (no history spam)
  • Proxy-aware: auto-detects /hero_os_ui prefix behind hero_proxy
  • Feature-gated: routing module only compiles with web-platform feature
  • Pure parsing: parse_url() is testable without browser globals

Next Step

Browser-level E2E route verification via hero_browser_mcp (#31).

## Routing Verification Report Verified the URL routing implementation on branch `development` (commit `ee2cfe6`). ### Implementation Status All 5 items from the implementation plan are **complete**: | # | Requirement | Status | Location | |---|---|---|---| | 1 | `mod routing;` in main.rs | Done | `hero_os_app/src/main.rs:27` (web-platform gated) | | 2 | Parse URL on mount, URL context wins over localStorage | Done | `main.rs:180-186` (initial_context) + `main.rs:703-743` (window restoration) | | 3 | `use_effect` syncs window state → URL via `replace_url()` | Done | `main.rs:792-811` | | 4 | `popstate` listener for browser back/forward | Done | `main.rs:813-877` | | 5 | URL wins over localStorage for app state | Done | URL-opened windows take priority | ### Unit Tests All **9 routing unit tests pass** (`cargo test -p hero_os_app routing`): - `test_parse_context_only` — `/space/default` - `test_parse_single_app` — `/space/default/contacts` - `test_parse_multiple_apps` — `/space/default/contacts+filesystem` - `test_parse_with_proxy_prefix` — `/hero_os_ui/space/workspace-1/contacts` - `test_parse_query_params` — `?contacts.id=abc123&contacts.view=detail` - `test_parse_root_returns_none` - `test_parse_no_space_prefix_returns_none` - `test_build_url_context_only` - `test_build_url_with_apps` ### Secondary Issue `livekit.js` path fixed from `/livekit.js` to `/hero_os_ui/livekit.js` in `Dioxus.toml` ✓ ### Architecture Summary - **Two-tier URL updates**: `push_url()` for discrete user actions (creates history entries) vs `replace_url()` for reactive sync (no history spam) - **Proxy-aware**: auto-detects `/hero_os_ui` prefix behind hero_proxy - **Feature-gated**: routing module only compiles with `web-platform` feature - **Pure parsing**: `parse_url()` is testable without browser globals ### Next Step Browser-level E2E route verification via hero_browser_mcp (#31).
Author
Owner

Browser-level E2E route tests now available on development_30 branch via make test-routes — see #31 for details.

Routing implementation is verified complete. This issue can be closed once the browser tests have been run successfully against a live instance.

Browser-level E2E route tests now available on `development_30` branch via `make test-routes` — see #31 for details. Routing implementation is verified complete. This issue can be closed once the browser tests have been run successfully against a live instance.
Author
Owner

E2E Browser Verification Complete

All URL routing verified via hero_browser_mcp against live hero_os_ui:

  • /hero_os_ui/ — loads, correct title
  • /hero_os_ui/space/geomind — URL preserved in browser bar
  • /hero_os_ui/space/geomind/contacts — URL preserved with app name
  • /hero_os_ui/space/geomind/contacts+filesystem — both app names in URL
  • /hero_os_ui/space/geomind/contacts?contacts.id=test123 — query params preserved
  • /hero_os_ui/space/work/filesystem — different context works
  • Browser back: returns to previous route URL correctly
  • Browser forward: returns to next route URL correctly
  • Invalid route (/space/nonexistent/bogus): no crash, no JS exceptions
  • No 404s on any route

16 passed, 0 failed. Routing implementation is solid.

## E2E Browser Verification Complete All URL routing verified via hero_browser_mcp against live hero_os_ui: - `/hero_os_ui/` — loads, correct title - `/hero_os_ui/space/geomind` — URL preserved in browser bar - `/hero_os_ui/space/geomind/contacts` — URL preserved with app name - `/hero_os_ui/space/geomind/contacts+filesystem` — both app names in URL - `/hero_os_ui/space/geomind/contacts?contacts.id=test123` — query params preserved - `/hero_os_ui/space/work/filesystem` — different context works - Browser back: returns to previous route URL correctly - Browser forward: returns to next route URL correctly - Invalid route (`/space/nonexistent/bogus`): no crash, no JS exceptions - No 404s on any route **16 passed, 0 failed.** Routing implementation is solid.
Sign in to join this conversation.
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/hero_os#30
No description provided.