implement routes #6

Closed
opened 2026-04-05 16:41:03 +00:00 by despiegk · 3 comments
Owner

make sure we implement

/hero_ui_routes

make sure we implement /hero_ui_routes
Author
Owner

Implementation Spec for Issue #6 — Hash-Based SPA Routing

Objective

Implement hash-based deep-linking across the entire hero_code_ui dashboard so that every meaningful view, object, and filter state is addressable by a canonical URL. Browser MCP agents and users must be able to bookmark, share, and directly navigate to any view by URL.

Routing model:

/#/<resource>
/#/<resource>/<id>
/#/<resource>/<id>/<subview>
/#/<resource>?<filters>

Requirements

  • Every top-level tab must have a canonical hash route (/#/jobs, /#/logs, /#/stats, /#/admin, /#/docs, /#/perf, /#/ide, /#/code)
  • Every job row must be clickable (entire row) and navigate to /#/jobs/<id>
  • Job detail view at /#/jobs/<id> must be independently loadable on page load
  • Filter/search state on Jobs tab must be route-backed: /#/jobs?status=running&engine=rhai&q=foo
  • Every log source must be linkable: /#/logs?src=<source>&level=3
  • Job logs reachable via /#/jobs/<id>/logs
  • Every repo row in Code tab clickable, navigating to /#/code/<encoded-path>
  • IDE tab encodes state: /#/ide?repo=<path>&file=<path>
  • Docs section navigation route-backed: /#/docs/<doc-id>
  • Browser back/forward must work correctly across all hash transitions
  • No full page reloads on hash navigation
  • Router must initialise on page load, reading initial hash and activating correct view

Files to Modify/Create

File Action Description
static/js/router.js Create New hash-router module — parses hash, dispatches to views, writes hash on navigation
static/js/dashboard.js Modify Wire router: tab switches, job row clicks, log source links, filter sync
static/js/code_manage.js Modify Make repo rows clickable, hash-to-repo resolution on load
static/js/code_editor.js Modify Sync IDE state (repo + file) to hash; restore from hash on activate
templates/index.html Modify Load router.js first; convert tab nav buttons to <a href="#/tab">

Implementation Plan

Step 1 — Create router.js: core hash router module

Files: static/js/router.js (new)

  • HeroRouter.parse(hash){ tab, id, subview, params }
  • HeroRouter.navigate(tab, id?, subview?, params?) — writes hash, uses history.pushState
  • HeroRouter.replaceState(...) — updates hash without adding history entry
  • HeroRouter.dispatch() — parses current hash, calls registered handler
  • HeroRouter.handlers — map of tab name → handler function
  • HeroRouter.init() — attaches hashchange + DOMContentLoaded listener
    Dependencies: none

Step 2 — Wire router into index.html and switchTab

Files: templates/index.html, static/js/dashboard.js

  • Load router.js before dashboard.js in <script> order
  • Convert tab nav <button onclick="switchTab('X')"> to <a href="#/X"> styled identically
  • Modify switchTab() to call HeroRouter.navigate() (guard against recursion)
  • Replace switchTab("jobs") in DOMContentLoaded with HeroRouter.init()
    Dependencies: Step 1

Step 3 — Jobs tab: row click, detail sub-route, filter params (dashboard.js)

Files: static/js/dashboard.js

  • Register HeroRouter.handlers.jobs
  • Make entire <tr> clickable → HeroRouter.navigate('jobs', jobId)
  • Engine and status values in rows become filter links
  • restoreJobFilters(params) reads params and sets filter controls
  • loadJobs() pushes current filter state into URL via replaceState
  • viewJobDetail() calls HeroRouter.navigate('jobs', jobId) at start
    Dependencies: Step 2

Step 4 — Logs tab: source and filter params in URL (dashboard.js)

Files: static/js/dashboard.js

  • Register HeroRouter.handlers.logs
  • restoreLogFilters(params) sets log filter controls
  • loadLogs() calls replaceState with current filter params
  • Log source badges become filter links /#/logs?src=<source>
    Dependencies: Step 2

Step 5 — Code tab: repo row click routes (code_manage.js)

Files: static/js/code_manage.js

  • Register HeroRouter.handlers.code
  • Repo rows get click handler → HeroRouter.navigate('code', encodeURIComponent(path))
    Dependencies: Step 2

Step 6 — IDE tab: repo + file state in URL (code_editor.js)

Files: static/js/code_editor.js

  • Register HeroRouter.handlers.ide
  • ideSelectRepo() calls replaceState after repo selected
  • File open calls replaceState with repo + file params
  • ideSelectRepoByPath() and ideOpenFileByPath() helpers for route restoration
    Dependencies: Step 2, Step 5

Step 7 — Docs tab: section routing (dashboard.js)

Files: static/js/dashboard.js, templates/index.html

  • Register HeroRouter.handlers.docs
  • Convert docs sidebar nav links to href="#/docs/<id>"
  • Docs content loader calls HeroRouter.navigate('docs', docId) on click
    Dependencies: Step 2

Step 8 — Simple tab routes: stats, admin, perf (dashboard.js)

Files: static/js/dashboard.js

  • Register trivial handlers for stats, admin, perf that just call switchTab(tab)
    Dependencies: Step 2

Steps 3–8 can run in parallel once Steps 1–2 are complete (each touches different code sections).


Acceptance Criteria

  • /#/jobs activates Jobs tab
  • /#/jobs/42 activates Jobs tab and opens job 42 detail
  • /#/jobs?status=running&engine=rhai activates Jobs with filters pre-set
  • /#/jobs/42/logs opens job 42's log view
  • /#/logs activates Logs tab
  • /#/logs?src=hero_code_server activates Logs with source pre-filtered
  • /#/stats, /#/admin, /#/perf activate their respective tabs
  • /#/docs/api-reference activates Docs and loads API Reference section
  • /#/ide?repo=/path/to/repo activates IDE with that repo selected
  • /#/ide?repo=/path/to/repo&file=/path/to/file.rhai opens that file
  • /#/code activates Code Management tab; /#/code/<encoded-path> selects that repo
  • Clicking a job row updates URL hash to /#/jobs/<id>
  • Clicking a repo row updates URL hash to /#/code/<encoded-path>
  • Changing Jobs filters updates hash query params
  • Browser back/forward navigates correctly between hash states
  • Page loaded at /#/logs?src=foo shows Logs tab with source pre-filtered (no extra click)
  • Engine badge in job row links to /#/jobs?engine=<engine>
  • Status badge in job row links to /#/jobs?status=<status>
  • Log source badge links to /#/logs?src=<source>
  • No full page reloads on any hash navigation
  • router.js loads before dashboard.js in index.html

Notes

  • Use history.pushState for forward navigation and history.replaceState for filter changes (avoids polluting back stack)
  • Guard against recursive hashchange triggers with a _navigating flag
  • Always encodeURIComponent repo paths in hash segments; decodeURIComponent when reading back
  • The /job-output and /file-editor standalone pages are out of scope (separate HTML pages opened via window.open())
  • BASE_PATH prefix does not affect client-side hashes — verified
# Implementation Spec for Issue #6 — Hash-Based SPA Routing ## Objective Implement hash-based deep-linking across the entire `hero_code_ui` dashboard so that every meaningful view, object, and filter state is addressable by a canonical URL. Browser MCP agents and users must be able to bookmark, share, and directly navigate to any view by URL. Routing model: ``` /#/<resource> /#/<resource>/<id> /#/<resource>/<id>/<subview> /#/<resource>?<filters> ``` --- ## Requirements - Every top-level tab must have a canonical hash route (`/#/jobs`, `/#/logs`, `/#/stats`, `/#/admin`, `/#/docs`, `/#/perf`, `/#/ide`, `/#/code`) - Every job row must be clickable (entire row) and navigate to `/#/jobs/<id>` - Job detail view at `/#/jobs/<id>` must be independently loadable on page load - Filter/search state on Jobs tab must be route-backed: `/#/jobs?status=running&engine=rhai&q=foo` - Every log source must be linkable: `/#/logs?src=<source>&level=3` - Job logs reachable via `/#/jobs/<id>/logs` - Every repo row in Code tab clickable, navigating to `/#/code/<encoded-path>` - IDE tab encodes state: `/#/ide?repo=<path>&file=<path>` - Docs section navigation route-backed: `/#/docs/<doc-id>` - Browser back/forward must work correctly across all hash transitions - No full page reloads on hash navigation - Router must initialise on page load, reading initial hash and activating correct view --- ## Files to Modify/Create | File | Action | Description | |---|---|---| | `static/js/router.js` | **Create** | New hash-router module — parses hash, dispatches to views, writes hash on navigation | | `static/js/dashboard.js` | **Modify** | Wire router: tab switches, job row clicks, log source links, filter sync | | `static/js/code_manage.js` | **Modify** | Make repo rows clickable, hash-to-repo resolution on load | | `static/js/code_editor.js` | **Modify** | Sync IDE state (repo + file) to hash; restore from hash on activate | | `templates/index.html` | **Modify** | Load router.js first; convert tab nav buttons to `<a href="#/tab">` | --- ## Implementation Plan ### Step 1 — Create `router.js`: core hash router module Files: `static/js/router.js` (new) - `HeroRouter.parse(hash)` → `{ tab, id, subview, params }` - `HeroRouter.navigate(tab, id?, subview?, params?)` — writes hash, uses `history.pushState` - `HeroRouter.replaceState(...)` — updates hash without adding history entry - `HeroRouter.dispatch()` — parses current hash, calls registered handler - `HeroRouter.handlers` — map of tab name → handler function - `HeroRouter.init()` — attaches `hashchange` + `DOMContentLoaded` listener Dependencies: none ### Step 2 — Wire router into `index.html` and `switchTab` Files: `templates/index.html`, `static/js/dashboard.js` - Load `router.js` before `dashboard.js` in `<script>` order - Convert tab nav `<button onclick="switchTab('X')">` to `<a href="#/X">` styled identically - Modify `switchTab()` to call `HeroRouter.navigate()` (guard against recursion) - Replace `switchTab("jobs")` in DOMContentLoaded with `HeroRouter.init()` Dependencies: Step 1 ### Step 3 — Jobs tab: row click, detail sub-route, filter params (dashboard.js) Files: `static/js/dashboard.js` - Register `HeroRouter.handlers.jobs` - Make entire `<tr>` clickable → `HeroRouter.navigate('jobs', jobId)` - Engine and status values in rows become filter links - `restoreJobFilters(params)` reads params and sets filter controls - `loadJobs()` pushes current filter state into URL via `replaceState` - `viewJobDetail()` calls `HeroRouter.navigate('jobs', jobId)` at start Dependencies: Step 2 ### Step 4 — Logs tab: source and filter params in URL (dashboard.js) Files: `static/js/dashboard.js` - Register `HeroRouter.handlers.logs` - `restoreLogFilters(params)` sets log filter controls - `loadLogs()` calls `replaceState` with current filter params - Log source badges become filter links `/#/logs?src=<source>` Dependencies: Step 2 ### Step 5 — Code tab: repo row click routes (code_manage.js) Files: `static/js/code_manage.js` - Register `HeroRouter.handlers.code` - Repo rows get click handler → `HeroRouter.navigate('code', encodeURIComponent(path))` Dependencies: Step 2 ### Step 6 — IDE tab: repo + file state in URL (code_editor.js) Files: `static/js/code_editor.js` - Register `HeroRouter.handlers.ide` - `ideSelectRepo()` calls `replaceState` after repo selected - File open calls `replaceState` with repo + file params - `ideSelectRepoByPath()` and `ideOpenFileByPath()` helpers for route restoration Dependencies: Step 2, Step 5 ### Step 7 — Docs tab: section routing (dashboard.js) Files: `static/js/dashboard.js`, `templates/index.html` - Register `HeroRouter.handlers.docs` - Convert docs sidebar nav links to `href="#/docs/<id>"` - Docs content loader calls `HeroRouter.navigate('docs', docId)` on click Dependencies: Step 2 ### Step 8 — Simple tab routes: stats, admin, perf (dashboard.js) Files: `static/js/dashboard.js` - Register trivial handlers for `stats`, `admin`, `perf` that just call `switchTab(tab)` Dependencies: Step 2 > Steps 3–8 can run in parallel once Steps 1–2 are complete (each touches different code sections). --- ## Acceptance Criteria - [ ] `/#/jobs` activates Jobs tab - [ ] `/#/jobs/42` activates Jobs tab and opens job 42 detail - [ ] `/#/jobs?status=running&engine=rhai` activates Jobs with filters pre-set - [ ] `/#/jobs/42/logs` opens job 42's log view - [ ] `/#/logs` activates Logs tab - [ ] `/#/logs?src=hero_code_server` activates Logs with source pre-filtered - [ ] `/#/stats`, `/#/admin`, `/#/perf` activate their respective tabs - [ ] `/#/docs/api-reference` activates Docs and loads API Reference section - [ ] `/#/ide?repo=/path/to/repo` activates IDE with that repo selected - [ ] `/#/ide?repo=/path/to/repo&file=/path/to/file.rhai` opens that file - [ ] `/#/code` activates Code Management tab; `/#/code/<encoded-path>` selects that repo - [ ] Clicking a job row updates URL hash to `/#/jobs/<id>` - [ ] Clicking a repo row updates URL hash to `/#/code/<encoded-path>` - [ ] Changing Jobs filters updates hash query params - [ ] Browser back/forward navigates correctly between hash states - [ ] Page loaded at `/#/logs?src=foo` shows Logs tab with source pre-filtered (no extra click) - [ ] Engine badge in job row links to `/#/jobs?engine=<engine>` - [ ] Status badge in job row links to `/#/jobs?status=<status>` - [ ] Log source badge links to `/#/logs?src=<source>` - [ ] No full page reloads on any hash navigation - [ ] `router.js` loads before `dashboard.js` in `index.html` --- ## Notes - Use `history.pushState` for forward navigation and `history.replaceState` for filter changes (avoids polluting back stack) - Guard against recursive `hashchange` triggers with a `_navigating` flag - Always `encodeURIComponent` repo paths in hash segments; `decodeURIComponent` when reading back - The `/job-output` and `/file-editor` standalone pages are out of scope (separate HTML pages opened via `window.open()`) - `BASE_PATH` prefix does not affect client-side hashes — verified
Author
Owner

Test Results

  • Status: PASS
  • Total: 91
  • Passed: 91
  • Failed: 0

Breakdown by crate

Crate Tests Result
hero_code_lib (unit) 44 ok
hero_code_sdk (unit) 5 ok
tests/job_lifecycle (integration) 9 ok
tests/rhai_execution (integration) 6 ok
tests/rpc_dispatch (integration) 27 ok
hero_code, hero_code_server, hero_code_ui, etc. 0 ok (no tests)

Build time: ~1m 18s
Note: The current changes are UI-only (JavaScript/HTML/CSS). No Rust tests cover those files, which explains the 0-test crates for hero_code_server and hero_code_ui. All existing Rust tests pass cleanly.

## Test Results - Status: PASS - Total: 91 - Passed: 91 - Failed: 0 ### Breakdown by crate | Crate | Tests | Result | |---|---|---| | `hero_code_lib` (unit) | 44 | ok | | `hero_code_sdk` (unit) | 5 | ok | | `tests/job_lifecycle` (integration) | 9 | ok | | `tests/rhai_execution` (integration) | 6 | ok | | `tests/rpc_dispatch` (integration) | 27 | ok | | `hero_code`, `hero_code_server`, `hero_code_ui`, etc. | 0 | ok (no tests) | **Build time:** ~1m 18s **Note:** The current changes are UI-only (JavaScript/HTML/CSS). No Rust tests cover those files, which explains the 0-test crates for `hero_code_server` and `hero_code_ui`. All existing Rust tests pass cleanly.
Author
Owner

Implementation Complete

Hash-based SPA routing has been implemented across the entire hero_code_ui dashboard.

Changes Made

New file:

  • static/js/router.js — Core window.HeroRouter module: parse(), navigate(), replaceState(), dispatch(), handlers map, init()

Modified files:

  • templates/index.html — Loads router.js before dashboard.js; tab nav buttons converted to <a href="#/tab"> anchors
  • static/js/dashboard.js — Registered route handlers for all tabs; job rows clickable with URL update; filter controls sync to URL; log source badges are filter links; viewJobDetail updates URL; restoreJobFilters and restoreLogFilters helpers; docs links updated
  • static/js/code_manage.js — Repo rows clickable with #/code/<encoded-path>; codeShowRepoDetail() helper
  • static/js/code_editor.js — IDE state (repo + file) synced to URL; ideSelectRepoByPath() and ideOpenFileByPath() helpers for route restoration

Route Coverage

Route Behaviour
/#/jobs Jobs tab
/#/jobs/42 Jobs tab + open job 42 detail
/#/jobs?status=running&engine=rhai Jobs tab with filters pre-set
/#/logs Logs tab
/#/logs?src=hero_code_server&level=3 Logs tab with source + level pre-filtered
/#/stats Stats tab
/#/admin Admin tab
/#/perf Perf tab
/#/docs/api-reference Docs tab, API Reference section
/#/ide?repo=/path/to/repo IDE tab with repo selected
/#/ide?repo=/path/to/repo&file=/path/to/file.rhai IDE with file open
/#/code Code Management tab
/#/code/<encoded-path> Code tab + repo detail

Test Results

  • Status: PASS
  • Total: 91 / Passed: 91 / Failed: 0
## Implementation Complete Hash-based SPA routing has been implemented across the entire `hero_code_ui` dashboard. ### Changes Made **New file:** - `static/js/router.js` — Core `window.HeroRouter` module: `parse()`, `navigate()`, `replaceState()`, `dispatch()`, `handlers` map, `init()` **Modified files:** - `templates/index.html` — Loads `router.js` before `dashboard.js`; tab nav buttons converted to `<a href="#/tab">` anchors - `static/js/dashboard.js` — Registered route handlers for all tabs; job rows clickable with URL update; filter controls sync to URL; log source badges are filter links; `viewJobDetail` updates URL; `restoreJobFilters` and `restoreLogFilters` helpers; docs links updated - `static/js/code_manage.js` — Repo rows clickable with `#/code/<encoded-path>`; `codeShowRepoDetail()` helper - `static/js/code_editor.js` — IDE state (repo + file) synced to URL; `ideSelectRepoByPath()` and `ideOpenFileByPath()` helpers for route restoration ### Route Coverage | Route | Behaviour | |---|---| | `/#/jobs` | Jobs tab | | `/#/jobs/42` | Jobs tab + open job 42 detail | | `/#/jobs?status=running&engine=rhai` | Jobs tab with filters pre-set | | `/#/logs` | Logs tab | | `/#/logs?src=hero_code_server&level=3` | Logs tab with source + level pre-filtered | | `/#/stats` | Stats tab | | `/#/admin` | Admin tab | | `/#/perf` | Perf tab | | `/#/docs/api-reference` | Docs tab, API Reference section | | `/#/ide?repo=/path/to/repo` | IDE tab with repo selected | | `/#/ide?repo=/path/to/repo&file=/path/to/file.rhai` | IDE with file open | | `/#/code` | Code Management tab | | `/#/code/<encoded-path>` | Code tab + repo detail | ### Test Results - Status: PASS - Total: 91 / Passed: 91 / Failed: 0
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_code#6
No description provided.