After testing the V09 redesign live on the Pi at onefinity.local
(1920x1080, Chrome fullscreen) several real bugs surfaced. This
commit fixes all of them.
Layout fits at 1920x1080
- Cap .app-shell at 100vh height with overflow:hidden so child
flex containers actually constrain to one screen.
- Make .control-page / .program-page / .console-page use
flex 1 1 auto + min-height 0 + overflow hidden so the page total
no longer grows to ~36 000 px when the gcode-viewer is mounted.
- Override clusterize.css default max-height: 200px on the
.clusterize-scroll element with max-height: none + flex 1 1 0 +
height 100% so the gcode listing fills the available column.
E-Stop in the header
- The legacy estop.pug SVG had width=130 height=130 but no
viewBox, so CSS-only sizing did nothing and the SVG content
spilled ~26 px off the right edge of the screen and ~70 px
below the header. Add viewBox="0 0 130 130" plus
preserveAspectRatio so CSS sizing actually shrinks the inner
geometry. Drop the octagonal clip-path (the SVG already
carries its own yellow safety ring + EMERGENCY/STOP text).
3D toolpath preview (path-viewer)
- The legacy .path-viewer.small CSS clamped the canvas to
340 x 150 floated into the corner. In the new program-body
grid we want it to fill the 600 px right column. Override
with width 100%, height auto, float none, !important.
- Make orbit.js wheel/touchstart/touchmove listeners
{passive: false} so OrbitControls.preventDefault() actually
works and the page no longer scrolls while panning the 3D
view on a touch screen.
Vue 1 template + reactivity bugs exposed by the live data
- Replace v-else-if (Vue 1 has no v-else-if) in
control-view.pug with three sibling v-if templates that
mutually exclude on w.enabled and state['2an'] == 3.
- axis-vars._get_motor_id: guard motor.axis.toLowerCase()
against undefined motors (initial config is [{}, {}, ...]).
- axis-vars._check_is_enabled: prefer config.motors[i].axis
when present, fall back to state[N + 'an'] only for
recognised axes (x/y/z/a) so undefined == undefined
doesn't mistakenly enable b/c rows.
- program-mixin: tolerate state.files / state.gcode_list
being undefined right after connect.
App-shell race conditions
- Skip the early parse_hash() in app.js ready() when the
initial hash is in the settings family. Those Svelte
components read settings.units / settings.probing-prompts /
motion.* etc. and crash on first paint with the empty
placeholder config. Stay on loading-view until update()
completes and routes us in itself.
Misc
- src/static/js/ui.js: null-guard the legacy burger menu code
(#menuLink no longer exists). Was throwing 'Cannot set
properties of null (setting onclick)'.
- src/static/css/Audiowide.css: switch the gstatic font URL
from http:// to https:// so it isn't blocked as mixed
content under the home.muehe.org HTTPS proxy.
- Macro buttons: drop the default 6 px yellow border-left.
The stripe now only appears via .has-color when
state.macros[i].color is actually configured. Removes the
asymmetric/lopsided look from the screenshot.
Tested live on http://10.1.10.55/ and via the HTTPS proxy at
https://onefinity.home.muehe.org/.
Implements the V09 mock end-to-end (per plans/2026-04-30_ux_redesign.md):
Top shell
- index.pug rebuilt around .app-shell with a slim 96px header.
- Underline-ribbon tab bar (Control / Program / Console / Settings)
replaces the old side menu and the inline #tab1..#tab4 system.
- Single 'All systems' pill collapses the legacy WiFi/Camera/Rotary/
IP/Version chip-soup into one popover (sys-popover) anchored to the
header; rotary toggle, camera feed and shutdown live there.
- Octagonal 88x88px STOP button wraps the existing <estop> SVG; STATE
pill with pulse-dot honors prefers-reduced-motion.
Routing
- app.js parse_hash maps every existing hash:
#control -> Control
#program / #program:auto -> Program
#console / #console:mdi|messages|indicators -> Console
#settings, #admin-general,
#admin-network, #motor:N, #tool, #io, #macros, #help,
#cheat-sheet -> Settings (rail picks inner)
- All deep links are preserved.
Control panel (control-view.pug + .js)
- 720px jog grid + 4-axis DRO + 4 KPI cards + 8-macro row.
- Jog tiles use V09 flat slate (#3f4b63) with diagonal helpers and
a ghost row for XY/Z origin shortcuts.
- Per-axis Settings/Set-zero/Home buttons grow to 72x72px.
- Status strip cards: State / Velocity-Feed / Spindle / Job. Tapping
the Spindle card opens the new override-drawer with feed + spindle
range inputs (resolved decision in plans/...).
- Macro row binds to state.macros.slice(0, 8); >8 lives in Settings.
- Drops the old <table> control-buttons, .info, .override and .tabs
blocks entirely.
Program panel (program-view.pug + .js)
- Extracts the Auto bar, file selectors, gcode-viewer and path-viewer
out of control-view.
- Action buttons (RUN/STOP/UPLOAD-FOLDER/UPLOAD-FILE/DOWNLOAD-FILE/
DELETE) at 84px with explicit color affordances.
- Reuses control-view's existing methods via the new program-mixin.
Console panel (console-view.pug + .js)
- Three sub-tabs: MDI / Messages / Indicators. Sub-tab persists in the
URL fragment (#console:messages etc.).
- MDI: terminal-style prompt + SEND, plus an 8-wide on-screen keypad
(G0/G1/G2/G3/G28/G92/M3/M5 + axis letters + CLEAR/SEND).
- Messages: pulls from .messages_log (mirrored from
state.messages); badge in the header tab counts unread.
- Indicators: mounts the existing <indicators> component.
Settings shell (settings-shell.pug + .js)
- New left rail navigator listing Display, Network, General/Firmware,
Spindle&Tool, IO, Motors 0..3, Macros, Cheat Sheet, Help.
- Inner area mounts the existing settings family templates via an
explicit v-if cascade (avoiding a Vue 1 :is reactivity quirk).
- Shutdown / Save buttons relocated from the dropped side menu.
JS plumbing
- main.js: Vue.config.async = false to keep dependent watchers in
sync when reactive data is mutated outside Vue's normal event loop
(e.g. from a hashchange listener).
- program-mixin.js extracted so control-view.js no longer carries the
file/macro/gcode methods that are now Program-only.
- control-view.js trimmed to jog/DRO/probe/home logic.
- console-view.js / settings-shell-view.js use a hashchange listener
+ local data props because Vue 1 cannot reliably observe
.sub_tab from a child component.
Stylus rewrite
- Removes the old .header (140px), .nav-header, .brand subtree, #menu,
#main, .control-view block, .info, .override, .toolbar, .macros-div,
.macros-button, the .tabs > input radio-tab system and the .control-
view #control media-query overrides. None of these are referenced
any more.
- Adds V09 tokens (jog/macro palette + accent + line/card colors) at
the top, the new shell rules, .ktab / .sys-btn / .state-badge /
.estop chrome, the .control-page grid, status strip + override
drawer, .program-page action / file bars and program body,
.console-page MDI keypad / messages / indicators panes, and the
.settings-shell rail.
- Adds a 1820px breakpoint that stacks the right column under the jog
on smaller portable monitors.
Hard cut: no config.ui.layout flag, the old shell is removed in this
single commit. side-menu.css is no longer included from index.pug.
Tested locally with agent-browser (1920x1080) on every top tab and
every settings sub-route; routing, active tab highlighting and inner
view selection all work without a controller connection.