You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The terminal bar (Status, Mayor, and Agent tabs) is hardcoded as a fixed-position panel at the bottom of the viewport with a non-resizable 300px content area. Users can't:
Move it to the top, left, or right
Resize it by dragging
Have the page content adjust dynamically (the layout uses a static pb-[340px] that doesn't respond to collapse state, wasting ~300px of vertical space when collapsed)
For users on wide monitors, a vertical orientation (terminal on the right) would be far more natural — similar to VS Code's panel system. Users on smaller screens might want a shorter terminal. The current one-size-fits-all layout doesn't serve anyone well.
Current Architecture
The terminal bar is position: fixed; right: 0; bottom: 0 with a dynamic left that tracks the sidebar width. Height toggles between 38px (collapsed) and 338px (expanded) via two hardcoded constants. Page content reserves space via a static pb-[340px] Tailwind class that never changes regardless of collapse state — meaning ~300px of dead space when collapsed.
Key files:
src/components/gastown/TerminalBar.tsx — the entire terminal bar component (position, tabs, content panes)
src/components/gastown/TerminalBarContext.tsx — state management (tabs, activeTab, collapsed)
src/app/(app)/organizations/[id]/gastown/[townId]/layout.tsx — same static padding
Solution
1. Configurable orientation and position
Users can place the terminal bar in any of four positions:
Position
Orientation
Terminal attaches to
Content adjusts
Bottom (default)
Horizontal
Bottom edge of viewport
padding-bottom
Top
Horizontal
Top edge of viewport (below any app header)
padding-top
Right
Vertical
Right edge of viewport
padding-right
Left
Vertical
Left edge of viewport (right of sidebar)
padding-left
A position picker (4-way toggle or dropdown) in the terminal bar's tab row lets users switch. Preference persisted to localStorage.
2. Drag-to-resize
A resize handle at the inner edge of the terminal bar (the edge facing the page content):
Bottom: horizontal handle at the top edge, cursor ns-resize
Top: horizontal handle at the bottom edge, cursor ns-resize
Right: vertical handle at the left edge, cursor ew-resize
Left: vertical handle at the right edge, cursor ew-resize
Drag events update the size in context. The xterm.js ResizeObserver in useXtermPty.ts already auto-fits the terminal when its container resizes — no additional xterm handling needed.
Min/max constraints to prevent the terminal from consuming the entire viewport:
Horizontal: min 100px, max 70% of viewport height
Vertical: min 200px, max 50% of viewport width
3. Dynamic page padding
Replace the static pb-[340px] with a dynamic inline style that responds to position, size, and collapse state:
This requires the layout's padding wrapper to consume TerminalBarContext. The layout is currently a Server Component — the padding div needs to be extracted into a client component wrapper.
When collapsed, only COLLAPSED_SIZE (the tab bar strip) is reserved — no dead space.
size: number (px — height for horizontal, width for vertical; default: 300)
setPosition(position) — updates position, persists to localStorage
setSize(size) — updates size, persists to localStorage
5. Vertical orientation adaptations
When the terminal is vertical (left/right), several sub-components need layout adjustments:
Component
Horizontal layout
Vertical layout
Tab bar
Horizontal flex row, overflow-x-auto
Vertical flex-col stack, overflow-y-auto
Tab items
Compact horizontal labels
Rotated text or icon-only with tooltip
Status pane (AlarmStatusPane)
Two-column layout (340px left + flex-1 right)
Single-column stacked layout
Terminal content
Full width, fixed height
Fixed width, full height
Collapse toggle
Up/down chevron
Left/right chevron
6. Sidebar conflict for left position
Placing the terminal on the left conflicts with the existing sidebar. The terminal should be positioned insideSidebarInset (the content area right of the sidebar), not outside it. For left position: left: sidebarWidth, same as the current bottom position calculates. This means the terminal pushes content right, not the sidebar.
7. DrawerStack interaction
The stackable drawers (DrawerStack.tsx) are fixed top-0 right-0 bottom-0 z-[61]. When the terminal is on the right, drawers need to respect the terminal's width — their right offset should be terminalSize + COLLAPSED_SIZE when the terminal is on the right. The terminal bar is z-50, drawers are z-61+, so layering is already correct.
Acceptance Criteria
Terminal bar can be positioned at bottom, top, right, or left
Position picker UI in the terminal bar
Drag-to-resize handle on the inner edge, with min/max constraints
Page content adjusts dynamically — no content hidden behind the terminal
Collapsed state only reserves the tab bar strip, not the full expanded size
Position and size persisted to localStorage
Vertical orientation: tab bar stacks vertically, status pane stacks single-column
DrawerStack respects terminal position when terminal is on the right
xterm.js auto-refits on resize (already handled by existing ResizeObserver)
Notes
No data migration needed
The existing ResizeObserver + fitAddon.fit() debounce in useXtermPty.ts means xterm terminals will auto-adapt to any resize — this is already working
Parent
Part of #204 (Phase 4: Hardening)
Problem
The terminal bar (Status, Mayor, and Agent tabs) is hardcoded as a fixed-position panel at the bottom of the viewport with a non-resizable 300px content area. Users can't:
pb-[340px]that doesn't respond to collapse state, wasting ~300px of vertical space when collapsed)For users on wide monitors, a vertical orientation (terminal on the right) would be far more natural — similar to VS Code's panel system. Users on smaller screens might want a shorter terminal. The current one-size-fits-all layout doesn't serve anyone well.
Current Architecture
The terminal bar is
position: fixed; right: 0; bottom: 0with a dynamicleftthat tracks the sidebar width. Height toggles between 38px (collapsed) and 338px (expanded) via two hardcoded constants. Page content reserves space via a staticpb-[340px]Tailwind class that never changes regardless of collapse state — meaning ~300px of dead space when collapsed.Key files:
src/components/gastown/TerminalBar.tsx— the entire terminal bar component (position, tabs, content panes)src/components/gastown/TerminalBarContext.tsx— state management (tabs, activeTab, collapsed)src/app/(app)/gastown/[townId]/layout.tsx— staticpb-[340px]paddingsrc/app/(app)/organizations/[id]/gastown/[townId]/layout.tsx— same static paddingSolution
1. Configurable orientation and position
Users can place the terminal bar in any of four positions:
padding-bottompadding-toppadding-rightpadding-leftA position picker (4-way toggle or dropdown) in the terminal bar's tab row lets users switch. Preference persisted to localStorage.
2. Drag-to-resize
A resize handle at the inner edge of the terminal bar (the edge facing the page content):
ns-resizens-resizeew-resizeew-resizeDrag events update the size in context. The xterm.js
ResizeObserverinuseXtermPty.tsalready auto-fits the terminal when its container resizes — no additional xterm handling needed.Min/max constraints to prevent the terminal from consuming the entire viewport:
3. Dynamic page padding
Replace the static
pb-[340px]with a dynamic inline style that responds to position, size, and collapse state:This requires the layout's padding wrapper to consume
TerminalBarContext. The layout is currently a Server Component — the padding div needs to be extracted into a client component wrapper.When collapsed, only
COLLAPSED_SIZE(the tab bar strip) is reserved — no dead space.4. New state in TerminalBarContext
Add to context:
position: 'bottom' | 'top' | 'right' | 'left'(default:'bottom')size: number(px — height for horizontal, width for vertical; default: 300)setPosition(position)— updates position, persists to localStoragesetSize(size)— updates size, persists to localStorage5. Vertical orientation adaptations
When the terminal is vertical (left/right), several sub-components need layout adjustments:
flexrow,overflow-x-autoflex-colstack,overflow-y-autoAlarmStatusPane)6. Sidebar conflict for left position
Placing the terminal on the left conflicts with the existing sidebar. The terminal should be positioned inside
SidebarInset(the content area right of the sidebar), not outside it. For left position:left: sidebarWidth, same as the current bottom position calculates. This means the terminal pushes content right, not the sidebar.7. DrawerStack interaction
The stackable drawers (
DrawerStack.tsx) arefixed top-0 right-0 bottom-0 z-[61]. When the terminal is on the right, drawers need to respect the terminal's width — theirrightoffset should beterminalSize + COLLAPSED_SIZEwhen the terminal is on the right. The terminal bar is z-50, drawers are z-61+, so layering is already correct.Acceptance Criteria
ResizeObserver)Notes
ResizeObserver+fitAddon.fit()debounce inuseXtermPty.tsmeans xterm terminals will auto-adapt to any resize — this is already workingMayorTerminalPaneduplicated xterm setup (identified in Fix terminal stability — WebSocket reconnection, resize debounce, control frame filtering #1195) should ideally be deduplicated before or alongside this work to avoid applying orientation changes in two placesCmd+Jlike VS Code's panel toggle)