Terminal UI: full-screen mode with collapsible navigation bar #71

Closed
opened 2026-04-29 11:15:12 +00:00 by mahmoud · 4 comments
Owner

Problem

The terminal UI does not have a proper full-screen mode. When going
full screen, the navigation bar disappears entirely, losing access to
other hero_proc features. We need to maximize terminal real estate
while keeping navigation accessible.

Requirements

  • Full-screen mode that maximizes the terminal area
  • Navigation bar should be collapsible (hide/show toggle) in full-screen
  • Or: navigation available as an overlay/sidebar that can be opened
    and closed without leaving full-screen
  • Should work well as a remote desktop replacement

Relevant code

  • crates/hero_proc_ui/ — Axum + Askama web dashboard templates

Acceptance Criteria

  • Full-screen mode available for terminal
  • Navigation bar can be shown/hidden while in full-screen
  • No navigation items permanently lost in full-screen mode
  • Works well as a primary remote working interface
## Problem The terminal UI does not have a proper full-screen mode. When going full screen, the navigation bar disappears entirely, losing access to other hero_proc features. We need to maximize terminal real estate while keeping navigation accessible. ## Requirements - Full-screen mode that maximizes the terminal area - Navigation bar should be collapsible (hide/show toggle) in full-screen - Or: navigation available as an overlay/sidebar that can be opened and closed without leaving full-screen - Should work well as a remote desktop replacement ## Relevant code - `crates/hero_proc_ui/` — Axum + Askama web dashboard templates ## Acceptance Criteria - [ ] Full-screen mode available for terminal - [ ] Navigation bar can be shown/hidden while in full-screen - [ ] No navigation items permanently lost in full-screen mode - [ ] Works well as a primary remote working interface
Member

Implementation Spec — Issue #71: Terminal full-screen mode with collapsible navigation

Objective

Make the Terminal page's full-screen mode preserve access to the top-level navigation (Home / Router / Terminal / Admin / Docs) instead of hiding it permanently. In full-screen, the navbar and tabs are auto-collapsed by default (so the terminal gets all the screen real estate) but can be revealed/hidden at any time via a toolbar button or keybinding, without leaving full-screen and without causing xterm.js to refit.

Requirements

From the issue:

  1. Full-screen mode that maximizes terminal area.
  2. Navigation must remain accessible — collapsible (show/hide) or available as overlay/sidebar.
  3. No nav items permanently lost in full-screen.
  4. Should work as a primary remote-desktop-style working interface.

Hard constraint:
5. The navbar links Home, Router, Terminal, Admin, Docs (rendered by partials/tabs.html) plus the top nav.navbar (rendered in base.html) must stay structurally intact. Hide/show is allowed; deletion or restructure is not.

Implicit / quality bar:
6. The current F11 keybinding and Esc-to-exit behavior must keep working.
7. The terminal-page sidebar polling and term-fullscreen body-class semantics must keep working.
8. xterm.js fit-addon should not need to recompute on every show/hide of nav (overlay, not in flow).
9. Show/hide preference must persist across reloads (localStorage).
10. No layout shift / visible reflow when toggling.

Files to Modify / Create

File Why
crates/hero_router/templates/terminal.html Replace the broken display: none !important rules in the page-local <style> block with overlay-based show/hide CSS keyed off a second body class (term-nav-revealed); add one toolbar button (#termNavToggle) next to #termFullscreen; add a row in the shortcuts modal describing the new key.
crates/hero_router/static/js/terminal.js Add revealNav() / hideNav() / toggleNav(); wire them to the new button, to a keybinding (Ctrl+Shift+N), and to localStorage; on enterFullscreen() apply the persisted preference (default = hidden); on exitFullscreen() clear the reveal state so non-fullscreen rendering is untouched.

No new files, no edits to base.html or partials/tabs.html.

Implementation Plan

Step 1 — Replace the broken full-screen nav-hiding CSS with an overlay scheme

Files: crates/hero_router/templates/terminal.html (page-local <style> block)

  • Remove the bug:
    body.term-fullscreen nav.navbar,
    body.term-fullscreen .nav-tabs { display: none !important; }
    
  • Replace with rules that take the navbar + tabs out of normal flow as a top-pinned overlay (position: fixed; z-index: 9500;) and translate them off-screen by default in full-screen:
    • body.term-fullscreen nav.navbar { transform: translateY(-100%); transition: transform 0.18s ease; }
    • Sibling rule for the tabs container: body.term-fullscreen nav.navbar + .container-fluid { transform: translateY(-100%); transition: transform 0.18s ease; }
    • Reveal state: body.term-fullscreen.term-nav-revealed nav.navbar { transform: translateY(0); } and the same for the sibling.
  • Visual polish: subtle bottom shadow on the navbar when revealed in full-screen.
  • Critically, do NOT use display: nonetransform keeps layout stable, no reflow, no fit-addon hit.

Dependencies: none.

Step 2 — Add the show/hide-nav toolbar button and shortcuts row

Files: crates/hero_router/templates/terminal.html (term-toolbar + shortcuts modal)

  • Insert a button immediately to the left of #termFullscreen:
    <button type="button" class="btn btn-sm btn-outline-secondary d-none" id="termNavToggle"
            title="Show / hide top navigation (Ctrl+Shift+N)">
      <i class="bi bi-layout-text-window-reverse" id="termNavToggleIcon"></i>
    </button>
    
  • d-none by default — JS toggles it visible only inside enterFullscreen().
  • Add a row to the shortcuts modal:
    <tr><td><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>N</kbd></td><td>Show / hide top navigation in fullscreen</td></tr>
    

Dependencies: Step 1.

Step 3 — Wire show/hide nav in terminal.js

Files: crates/hero_router/static/js/terminal.js

  • Add localStorage key constant: var NAV_REVEAL_KEY = 'hero_router.term.fullscreen.navRevealed';
  • Add three small helpers next to enterFullscreen / exitFullscreen:
    • revealNav() — adds term-nav-revealed to <body>, swaps the toolbar icon, writes '1' to localStorage. No fitAllPanes() call.
    • hideNav() — removes the class, swaps icon back, writes '0'.
    • toggleNav() — flips based on current class state.
  • Modify enterFullscreen():
    • Un-hide the toolbar button (document.getElementById('termNavToggle').classList.remove('d-none')).
    • Read localStorage; if '1', call revealNav(); else ensure the class is removed (default = hidden).
  • Modify exitFullscreen():
    • Remove term-nav-revealed from body.
    • Re-hide the toolbar button (.classList.add('d-none')).
  • In the bottom event-wiring block: bind #termNavToggle click → toggleNav().
  • In the unified keydown handler: inside the existing Ctrl+Shift arm, add if (e.code === 'KeyN') { e.preventDefault(); if (isFullscreen) toggleNav(); return; }. Gate on isFullscreen so it's a no-op outside full-screen.
  • Do NOT call fitAllPanes() from revealNav / hideNav — overlay = stable geometry.

Dependencies: Steps 1 and 2.

Acceptance Criteria

  • Pressing F11 (or clicking the fullscreen button) on the Terminal page enters full-screen and the terminal pane fills the viewport.
  • In full-screen, by default the top navbar and the Home/Router/Terminal/Admin/Docs tabs are hidden so the terminal has maximum space.
  • In full-screen, an always-visible button (#termNavToggle) and Ctrl+Shift+N reveal the navbar + tabs as an overlay; pressing again hides them. F11 / Esc continues to exit full-screen entirely.
  • Revealing/hiding the nav does NOT cause the terminal canvas to flicker, resize, or reflow.
  • The persisted preference is read on entering full-screen, so reload-then-re-enter restores the user's last choice.
  • Outside full-screen, the navbar and tabs render exactly as before.
  • All five top-level nav links (Home, Router, Terminal, Admin, Docs) remain reachable in every mode.
  • The shortcuts modal (? key) lists the new keybinding.

Notes / Pitfalls

  • Don't display: none the navbar. That's the existing bug; it's why nav is currently lost. Use transform: translateY(-100%) so the elements stay in the accessibility tree and toggling them costs zero layout.
  • Don't refit xterm on toggle. Because term-layout is position: fixed; inset: 0 in fullscreen and the navbar is overlaid (also position: fixed), the terminal's box never changes size when the nav reveals/hides. No fitAllPanes() call is needed in revealNav/hideNav.
  • Sidebar polling is unaffected — that's the terminal-sessions sidebar, not the top tabs.
  • Esc semantics unchanged — exits fullscreen entirely; does NOT toggle nav. Toggling is Ctrl+Shift+N and the toolbar button only.
  • Adjacent-sibling selector for the tabs partial. Selecting the tabs wrapper with body.term-fullscreen nav.navbar + .container-fluid works because partials/tabs.html is {% include %}d immediately after the navbar in base.html. A comment in the CSS will note this dependency.
  • Hash-based deep-linking still works. #fullscreen=1 continues to enter fullscreen on load; the nav-revealed state is per-user preference (localStorage), not in the URL.
  • Theme. Use Bootstrap CSS variables (var(--bs-body-bg)) so the overlay matches the active theme.
  • Scope. No changes to base.html, partials/tabs.html, partials/sidebar.html, or any Rust handler. Total diff target: under 100 lines.
# Implementation Spec — Issue #71: Terminal full-screen mode with collapsible navigation ## Objective Make the Terminal page's full-screen mode preserve access to the top-level navigation (Home / Router / Terminal / Admin / Docs) instead of hiding it permanently. In full-screen, the navbar and tabs are auto-collapsed by default (so the terminal gets all the screen real estate) but can be revealed/hidden at any time via a toolbar button or keybinding, *without* leaving full-screen and *without* causing xterm.js to refit. ## Requirements **From the issue:** 1. Full-screen mode that maximizes terminal area. 2. Navigation must remain accessible — collapsible (show/hide) or available as overlay/sidebar. 3. No nav items permanently lost in full-screen. 4. Should work as a primary remote-desktop-style working interface. **Hard constraint:** 5. The navbar links **Home, Router, Terminal, Admin, Docs** (rendered by `partials/tabs.html`) plus the top `nav.navbar` (rendered in `base.html`) must stay structurally intact. Hide/show is allowed; deletion or restructure is not. **Implicit / quality bar:** 6. The current F11 keybinding and `Esc`-to-exit behavior must keep working. 7. The terminal-page sidebar polling and `term-fullscreen` body-class semantics must keep working. 8. xterm.js fit-addon should not need to recompute on every show/hide of nav (overlay, not in flow). 9. Show/hide preference must persist across reloads (localStorage). 10. No layout shift / visible reflow when toggling. ## Files to Modify / Create | File | Why | |---|---| | `crates/hero_router/templates/terminal.html` | Replace the broken `display: none !important` rules in the page-local `<style>` block with overlay-based show/hide CSS keyed off a second body class (`term-nav-revealed`); add one toolbar button (`#termNavToggle`) next to `#termFullscreen`; add a row in the shortcuts modal describing the new key. | | `crates/hero_router/static/js/terminal.js` | Add `revealNav()` / `hideNav()` / `toggleNav()`; wire them to the new button, to a keybinding (`Ctrl+Shift+N`), and to localStorage; on `enterFullscreen()` apply the persisted preference (default = hidden); on `exitFullscreen()` clear the reveal state so non-fullscreen rendering is untouched. | No new files, no edits to `base.html` or `partials/tabs.html`. ## Implementation Plan ### Step 1 — Replace the broken full-screen nav-hiding CSS with an overlay scheme **Files:** `crates/hero_router/templates/terminal.html` (page-local `<style>` block) - Remove the bug: ```css body.term-fullscreen nav.navbar, body.term-fullscreen .nav-tabs { display: none !important; } ``` - Replace with rules that take the navbar + tabs out of normal flow as a top-pinned overlay (`position: fixed; z-index: 9500;`) and translate them off-screen by default in full-screen: - `body.term-fullscreen nav.navbar { transform: translateY(-100%); transition: transform 0.18s ease; }` - Sibling rule for the tabs container: `body.term-fullscreen nav.navbar + .container-fluid { transform: translateY(-100%); transition: transform 0.18s ease; }` - Reveal state: `body.term-fullscreen.term-nav-revealed nav.navbar { transform: translateY(0); }` and the same for the sibling. - Visual polish: subtle bottom shadow on the navbar when revealed in full-screen. - Critically, do NOT use `display: none` — `transform` keeps layout stable, no reflow, no fit-addon hit. **Dependencies:** none. ### Step 2 — Add the show/hide-nav toolbar button and shortcuts row **Files:** `crates/hero_router/templates/terminal.html` (term-toolbar + shortcuts modal) - Insert a button immediately to the left of `#termFullscreen`: ```html <button type="button" class="btn btn-sm btn-outline-secondary d-none" id="termNavToggle" title="Show / hide top navigation (Ctrl+Shift+N)"> <i class="bi bi-layout-text-window-reverse" id="termNavToggleIcon"></i> </button> ``` - `d-none` by default — JS toggles it visible only inside `enterFullscreen()`. - Add a row to the shortcuts modal: ```html <tr><td><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>N</kbd></td><td>Show / hide top navigation in fullscreen</td></tr> ``` **Dependencies:** Step 1. ### Step 3 — Wire show/hide nav in `terminal.js` **Files:** `crates/hero_router/static/js/terminal.js` - Add localStorage key constant: `var NAV_REVEAL_KEY = 'hero_router.term.fullscreen.navRevealed';` - Add three small helpers next to `enterFullscreen` / `exitFullscreen`: - `revealNav()` — adds `term-nav-revealed` to `<body>`, swaps the toolbar icon, writes `'1'` to localStorage. **No `fitAllPanes()` call.** - `hideNav()` — removes the class, swaps icon back, writes `'0'`. - `toggleNav()` — flips based on current class state. - Modify `enterFullscreen()`: - Un-hide the toolbar button (`document.getElementById('termNavToggle').classList.remove('d-none')`). - Read localStorage; if `'1'`, call `revealNav()`; else ensure the class is removed (default = hidden). - Modify `exitFullscreen()`: - Remove `term-nav-revealed` from body. - Re-hide the toolbar button (`.classList.add('d-none')`). - In the bottom event-wiring block: bind `#termNavToggle` click → `toggleNav()`. - In the unified keydown handler: inside the existing Ctrl+Shift arm, add `if (e.code === 'KeyN') { e.preventDefault(); if (isFullscreen) toggleNav(); return; }`. Gate on `isFullscreen` so it's a no-op outside full-screen. - Do NOT call `fitAllPanes()` from `revealNav` / `hideNav` — overlay = stable geometry. **Dependencies:** Steps 1 and 2. ## Acceptance Criteria - [ ] Pressing F11 (or clicking the fullscreen button) on the Terminal page enters full-screen and the terminal pane fills the viewport. - [ ] In full-screen, by default the top navbar and the Home/Router/Terminal/Admin/Docs tabs are hidden so the terminal has maximum space. - [ ] In full-screen, an always-visible button (`#termNavToggle`) and `Ctrl+Shift+N` reveal the navbar + tabs as an overlay; pressing again hides them. F11 / `Esc` continues to exit full-screen entirely. - [ ] Revealing/hiding the nav does NOT cause the terminal canvas to flicker, resize, or reflow. - [ ] The persisted preference is read on entering full-screen, so reload-then-re-enter restores the user's last choice. - [ ] Outside full-screen, the navbar and tabs render exactly as before. - [ ] All five top-level nav links (Home, Router, Terminal, Admin, Docs) remain reachable in every mode. - [ ] The shortcuts modal (`?` key) lists the new keybinding. ## Notes / Pitfalls - **Don't `display: none` the navbar.** That's the existing bug; it's why nav is currently lost. Use `transform: translateY(-100%)` so the elements stay in the accessibility tree and toggling them costs zero layout. - **Don't refit xterm on toggle.** Because `term-layout` is `position: fixed; inset: 0` in fullscreen and the navbar is overlaid (also `position: fixed`), the terminal's box never changes size when the nav reveals/hides. No `fitAllPanes()` call is needed in `revealNav`/`hideNav`. - **Sidebar polling is unaffected** — that's the terminal-sessions sidebar, not the top tabs. - **`Esc` semantics unchanged** — exits fullscreen entirely; does NOT toggle nav. Toggling is `Ctrl+Shift+N` and the toolbar button only. - **Adjacent-sibling selector for the tabs partial.** Selecting the tabs wrapper with `body.term-fullscreen nav.navbar + .container-fluid` works because `partials/tabs.html` is `{% include %}`d immediately after the navbar in `base.html`. A comment in the CSS will note this dependency. - **Hash-based deep-linking still works.** `#fullscreen=1` continues to enter fullscreen on load; the nav-revealed state is per-user preference (localStorage), not in the URL. - **Theme.** Use Bootstrap CSS variables (`var(--bs-body-bg)`) so the overlay matches the active theme. - **Scope.** No changes to `base.html`, `partials/tabs.html`, `partials/sidebar.html`, or any Rust handler. Total diff target: under 100 lines.
Member

Test Results

  • Build: ok
  • Total tests: 94
  • Passed: 94
  • Failed: 0
  • Ignored: 0

The changes in this branch are limited to crates/hero_router/templates/terminal.html and crates/hero_router/static/js/terminal.js (HTML/JS only). No Rust unit or integration test exercises these template/static assets directly, so the test suite confirms the Rust crate's build/test surface is unchanged but does not provide direct coverage of the terminal fullscreen / collapsible nav UI changes themselves.

## Test Results - Build: ok - Total tests: 94 - Passed: 94 - Failed: 0 - Ignored: 0 The changes in this branch are limited to `crates/hero_router/templates/terminal.html` and `crates/hero_router/static/js/terminal.js` (HTML/JS only). No Rust unit or integration test exercises these template/static assets directly, so the test suite confirms the Rust crate's build/test surface is unchanged but does not provide direct coverage of the terminal fullscreen / collapsible nav UI changes themselves.
Member

Implementation summary

Implemented the full-screen mode with a collapsible top navigation bar on the Terminal page. The five top-level navigation links (Home, Router, Terminal, Admin, Docs) are kept structurally intact in every mode — only their visibility while the page is in full-screen toggles, via an overlay scheme.

How it works

  • In full-screen, the navbar and the tabs container are taken out of normal flow (position: fixed, z-index: 9500) and translated off-screen (transform: translateY(-100%)). They are NOT display: none, so they remain in the accessibility tree and toggling them costs zero layout.
  • A new toolbar button (#termNavToggle) appears next to the existing fullscreen button while in full-screen, and a new keybinding Ctrl+Shift+N toggles the navbar's visibility without leaving full-screen.
  • The reveal/hide preference is persisted in localStorage (hero_router.term.fullscreen.navRevealed) so reload-then-re-enter restores the user's last choice. Default is hidden, maximizing terminal real estate.
  • xterm.js is NOT refit when toggling the navbar — the terminal's bounding box (position: fixed; inset: 0) does not change because the navbar is overlaid on top of the terminal layer.

Files modified

  • crates/hero_router/templates/terminal.html — replaced the broken display: none !important rules with an overlay-based show/hide CSS scheme keyed on a second body class term-nav-revealed; added the #termNavToggle button to the toolbar; added a shortcuts-modal row for Ctrl+Shift+N.
  • crates/hero_router/static/js/terminal.js — added revealNav() / hideNav() / toggleNav() helpers, wired them to the new button, the new Ctrl+Shift+N keybinding, and localStorage; updated enterFullscreen() / exitFullscreen() to un-hide / re-hide the toolbar button and restore the persisted reveal state on entry.

No changes to base.html, partials/tabs.html, partials/sidebar.html, or any Rust handler.

Test results

  • Build: ok (cargo build -p hero_router)
  • Tests: 94 passed, 0 failed, 0 ignored
  • The changes are HTML / JS only; no Rust test directly exercises the terminal UI behavior. The Rust test surface is unchanged — passing tests confirm no regression in any code path the build pulls in. Browser-level verification of the toggle behavior is recommended as a follow-up.

Acceptance criteria

  • Full-screen mode available for terminal (preserved from existing F11 flow)
  • Navigation bar can be shown/hidden while in full-screen (#termNavToggle and Ctrl+Shift+N)
  • No navigation items permanently lost in full-screen mode (overlay, not display: none)
  • Works as a primary remote working interface (terminal owns full viewport by default; nav is one click / one keystroke away)
## Implementation summary Implemented the full-screen mode with a collapsible top navigation bar on the Terminal page. The five top-level navigation links (Home, Router, Terminal, Admin, Docs) are kept structurally intact in every mode — only their visibility while the page is in full-screen toggles, via an overlay scheme. ### How it works - In full-screen, the navbar and the tabs container are taken out of normal flow (`position: fixed`, `z-index: 9500`) and translated off-screen (`transform: translateY(-100%)`). They are NOT `display: none`, so they remain in the accessibility tree and toggling them costs zero layout. - A new toolbar button (`#termNavToggle`) appears next to the existing fullscreen button while in full-screen, and a new keybinding `Ctrl+Shift+N` toggles the navbar's visibility without leaving full-screen. - The reveal/hide preference is persisted in `localStorage` (`hero_router.term.fullscreen.navRevealed`) so reload-then-re-enter restores the user's last choice. Default is hidden, maximizing terminal real estate. - xterm.js is NOT refit when toggling the navbar — the terminal's bounding box (`position: fixed; inset: 0`) does not change because the navbar is overlaid on top of the terminal layer. ### Files modified - `crates/hero_router/templates/terminal.html` — replaced the broken `display: none !important` rules with an overlay-based show/hide CSS scheme keyed on a second body class `term-nav-revealed`; added the `#termNavToggle` button to the toolbar; added a shortcuts-modal row for `Ctrl+Shift+N`. - `crates/hero_router/static/js/terminal.js` — added `revealNav()` / `hideNav()` / `toggleNav()` helpers, wired them to the new button, the new `Ctrl+Shift+N` keybinding, and `localStorage`; updated `enterFullscreen()` / `exitFullscreen()` to un-hide / re-hide the toolbar button and restore the persisted reveal state on entry. No changes to `base.html`, `partials/tabs.html`, `partials/sidebar.html`, or any Rust handler. ### Test results - Build: ok (`cargo build -p hero_router`) - Tests: 94 passed, 0 failed, 0 ignored - The changes are HTML / JS only; no Rust test directly exercises the terminal UI behavior. The Rust test surface is unchanged — passing tests confirm no regression in any code path the build pulls in. Browser-level verification of the toggle behavior is recommended as a follow-up. ### Acceptance criteria - [x] Full-screen mode available for terminal (preserved from existing F11 flow) - [x] Navigation bar can be shown/hidden while in full-screen (`#termNavToggle` and `Ctrl+Shift+N`) - [x] No navigation items permanently lost in full-screen mode (overlay, not `display: none`) - [x] Works as a primary remote working interface (terminal owns full viewport by default; nav is one click / one keystroke away)
Member

Pull request opened: #82

This PR implements the changes discussed in this issue.

Pull request opened: https://forge.ourworld.tf/lhumina_code/hero_router/pulls/82 This PR implements the changes discussed in this issue.
mahmoud added this to the ACTIVE project 2026-05-10 06:42:36 +00:00
mahmoud added this to the now milestone 2026-05-10 06:42:39 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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_router#71
No description provided.