fix(console): #140 bug fixes + multi-session manager (tabs/dock/popout) - #141
Merged
Conversation
- Removed the console column from the grid and replaced it with a tools column for better organization. - Updated the console opening logic to use box_id instead of id for consistency. - Enhanced the console.js file to support multi-session management with a new ConsoleSession class. - Improved the layout handling for console sessions, including dock and drawer modes. - Added preference management for font size and dock height. - Implemented a more robust session lifecycle management, including connection handling and error reporting.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes the /bx console-launch regressions from #140 and introduces a rewritten multi-session console UI (tabs + dock + popout) built around a ConsoleSession/ConsoleManager architecture. It also adds a new server route + handler + templ page to support a chromeless console popout window.
Changes:
- Fix bx-grid console actions: correct
box_idusage, prevent Tools-cell clicks from triggering row navigation, and move the shell icon into a dedicated Tools column. - Replace the single-session console drawer JS with a multi-session manager supporting tabs, drawer/dock layouts, persisted preferences, and popout.
- Add popout page plumbing (route, handler, templ page) and new console panel CSS (layouts, tabs, status dot, dock resize handle).
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| gearbox/static/js/console/console.js | Rewritten console client into session/manager model with tabs, layout modes, popout integration, and persisted prefs. |
| gearbox/static/js/bx/bx-page.js | Fixes console launch bugs (wrong ID field, rowClick interference) and adds a Tools column. |
| gearbox/static/css/components/console.css | New stylesheet for console chrome: dock/drawer positioning, tabs, status dot, dock resize handle, popout mode. |
| gearbox/internal/framework/templates/components/console.templ | Updates ConsoleDrawer markup for new toolbar/tab UI and loads console.css. |
| gearbox/internal/framework/templates/pages/console_popout.templ | New standalone chromeless page that boots the console manager in popout mode. |
| gearbox/internal/framework/handler/console_popout.go | New handler to serve the popout page with auth + box gating. |
| gearbox/cmd/server/main.go | Registers /console/popout/{boxID} route. |
- ARIA tab semantics: move role="tab" onto the label button so nested interactive elements (dot, close) are siblings of the tabbable element, not nested inside it. Wrapper is now role="presentation". - Per-tab status dot: add aria-label that reflects current state and reconnect action; aria-disabled when inert. - Toolbar buttons (clear, layout-drawer/dock/popout, close, +): add explicit aria-label; mark icon SVGs aria-hidden so screen readers don't try to announce path data. - Capabilities fetch failures and websocket errors now also write a red [console] line into the xterm buffer, so a failing session isn't a silent black void — matches the existing 'err' frame path. - Dock resize: switch from mouse events to Pointer Events with setPointerCapture/releasePointerCapture, plus blur and visibilitychange cleanups. Prevents stuck drag state when the mouse releases outside the window. - Fix CSS selector for .xterm height: actual DOM is .console-xterm-pad > #console-xterm > .console-session-host > .xterm — old selector skipped the #console-xterm wrapper, so the rule was inert and the terminal didn't always fill its container. - Popout templ doc comment was stale (mentioned overriding the close button); CSS now hides the entire header in popout mode, comment updated to match. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #140.
Started as the bug fixes from #140 (404 on shell icon, drop-to-Home navigation, Tools-column placement). User asked to bundle scope-creep QoL work in the same PR — multi-session/tabs/dock/popout/font-slider — since they prefer one big diff over many small ones. Result: full console-manager rewrite layered on top of the original fixes.
Bug fixes (original #140 scope)
d.id→d.box_idat three call sites instatic/js/bx/bx-page.js(cellClick, palette command id, palette run).BoxRow.JSONfieldisbox_id, notid— old code sent/api/console/undefined/capabilities→ 404.grid.on('rowClick', …)against clicks originating in the Tools column. Tabulator firesrowClickfrom its own event delegation independent of DOM bubbling, soe.stopPropagation()insidecellClickwas not enough.frozen: true; switched to unfrozen after the user pointed out the Tabulator divider was ugly.Reusable console plumbing
static/js/console/console.jsrewritten as aConsoleSession(one PTY + xterm + WS) plus aConsoleManager(chrome / tabs / layout / prefs). Sessions take a descriptor:{ kind, boxID, … }. Adding container-exec for the future Docker gear means a newkind: 'container'case inwsURLForDescriptor+capsURLForDescriptor— the chrome and tabs come along for free.Layouts
localStorage["gearbox.console.dockHeight"]. Pure-CSS sibling selector keeps the dock anchored to the right of the sidebar (#main-content.ml-16 ~ #console-drawer.console-layout-dock { left: 4rem }), with atransition: left 0.3sto track the sidebar's own collapse animation.window.open('/console/popout/{boxID}', '_blank', 'popup=yes …')opens a chromeless window driven bypages.ConsolePopoutPage. Authentication + per-box opt-in gating matchAPIConsoleCapabilities. In popout mode all chrome is hidden (.console-popout .console-header,.console-tab-bar,.console-dock-handle { display: none }) so the terminal fills the window.Drawer ↔ dock are mutually exclusive — only the inactive layout's button is visible in the toolbar. Popout button stays put.
Tabs
#Nsuffix at render time; closing one of two clones renames the survivor back.Header + toolbar
uidis recoverable withwhoami,modeis a single value today (revisit if we add an SSH-bridge mode).localStorage, matches logs-viewer pattern), Clear, layout-switch (drawer-or-dock, mutually exclusive), popout, close.py-2→py-1), dock resize handle dropped from 6px → 5px.Preferences
All under
gearbox.console.*:fontSize(int 10..24, default 13) — applied to all sessions immediatelydockHeight(int px, default 320, min 160)layout("drawer" | "dock", default "drawer") — popout is transient, never persistedFiles
Test plan
Light Hugger #1/#2.Light Hugger.Cmd-Wcloses the popout window cleanly.Esccloses the in-page console (or just the active tab in dock mode with ≥ 2 sessions); Esc is a no-op inside a popout window.box_console:viewgets 403 on/console/popout/{boxID}.console_enabled: falsereturns 404 on/console/popout/{boxID}and the bx grid does not show the shell icon for it.🤖 Generated with Claude Code