Skip to content

feat: Dynamic multi-terminal UI with split-pane support#6

Open
VivanRajath wants to merge 2 commits into
open-gitagent:mainfrom
VivanRajath:feature/ui-improvements
Open

feat: Dynamic multi-terminal UI with split-pane support#6
VivanRajath wants to merge 2 commits into
open-gitagent:mainfrom
VivanRajath:feature/ui-improvements

Conversation

@VivanRajath
Copy link
Copy Markdown

Summary

Adds a fully dynamic multi-terminal interface to the ClawLess terminal panel. Users can now open multiple independent shell sessions, rename them inline, close them individually, and view them in a split-pane layout alongside the Agent terminal — all without any page reload.

Motivation

The original UI had a single fixed terminal pane tied to the agent. Power users working with ClawLess often need a separate shell for running commands while the agent is active. This PR addresses that gap with a scalable, tab-based terminal system.

File Changes

File What changed
src/container.ts Replaced fixed shellProcess2/shellWriter2 fields with a Map<string, WebContainerProcess> and a generic spawnShell(id, terminal) method supporting unlimited concurrent shells
src/sdk.ts Added spawnShell(id, terminal) passthrough; removed hardcoded _terminal2 and startShell2 calls
src/ui.ts Rewrote bindTerminalTabs() — dynamic tab creation, inline rename (double-click), tab close (×), split-pane toggle, and drag-to-resize handle
index.html Updated terminal panel markup: ⚙ Agent tab + button + split button; cleaner pane structure
src/style.css New styles for dynamic tabs, close button (hidden until hover), button, inline rename via contenteditable, scrollable overflow tab bar, split-pane layout

How to Test

Environment: Windows 11, Node.js v20, Chrome 123

  1. npm install && npm run dev
  2. Open http://localhost:5173 in Chrome
  3. Wait for the boot sequence to complete (agent installs)

Tab creation:

  • Click → "Terminal 1" tab appears; a live jsh shell launches inside the WebContainer
  • Type ls → lists workspace files independently from the agent
  • Click again → "Terminal 2" opens with another independent shell
  • Both terminals run simultaneously without interfering with each other

Rename:

  • Double-click "Terminal 1" tab label → it becomes editable (cursor appears)
  • Type "Server" → press Enter → label saves as "Server"
  • Click away also saves the rename

Close:

  • Hover any dynamic tab → × button appears
  • Click × → tab and its shell pane are removed; view switches back to Agent

Split view:

  • Click → terminal panel splits: active shell on right, Agent on left
  • Drag the divider between panes to resize
  • Click again → returns to single-pane view

No regressions:

  • Agent / gitclaw terminal works normally throughout
  • npm run build passes with zero TypeScript errors
  • No console errors observed

Checklist

  • npm run build passes
  • No TypeScript errors (strict mode enabled)
  • Manually tested on Windows 11 / Chrome
  • Backward compatible — existing startShell / startGitclaw flow untouched
  • No new dependencies added

Copy link
Copy Markdown
Contributor

@shreyas-lyzr shreyas-lyzr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Large feature PR — multi-terminal UI with split-pane support. The concept is solid and addresses a real gap for power users. A few things need attention before this is merge-ready:

Blocking — dependency regression

The package-lock.json downgrades monaco-editor from ^0.55.1 to ^0.53.0 and bumps vite from ^5.2.0 to ^8.0.1. The vite major bump is especially risky — vite 8 introduced breaking config changes. If this PR was developed on a different base branch, please rebase onto current main and re-lock. The lockfile changes will likely conflict once rebased.

New clawcontainer dependency

clawcontainer was added without explanation in the PR body. What does it provide and is it a trusted/internal package? A note on this would help reviewers.

Split-pane state on page reload

The new tab/pane state appears to live entirely in JS memory. If the user reloads, all open terminals and their layout disappear. Is that the intended behavior, or should open sessions persist in localStorage?

Accessibility

The new tab buttons use emoji characters (, , ) as their visible label with no aria-label. Screen readers will get meaningless characters. Add aria-label attributes to the interactive buttons.

Terminal lifecycle

When a tab is closed, is the associated WebContainer process explicitly terminated? If not, processes will leak silently in the background.

The overall direction is good. Addressing the dependency regression and clarifying the clawcontainer addition are the minimum requirements before merge.

Copy link
Copy Markdown
Contributor

@shreyas-lyzr shreyas-lyzr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Large, well-structured PR with a clear purpose. The multi-terminal tab system is a meaningful UX improvement and the code is generally solid. A few issues need attention before merge.

Blocking: onData listener accumulation in spawnShell

In container.ts, spawnShell registers terminal.onData(...) without disposing the previous one. Because TerminalManager.onData in this PR does dispose-before-register (via onDataDisposable), each new shell spawned to the same terminal will silently detach the previous shell's listener and attach the new one. This means switching between shells that share a TerminalManager instance will break input routing for all but the last-spawned shell. If each dynamic tab gets its own TerminalManager instance, this is fine — but that invariant is not enforced or documented, and the Map<string, TerminalManager> in UIManager suggests it might not always hold.

Blocking: window.addEventListener('resize') leak in spawnShell

Each call to spawnShell adds a new resize event listener on window with no corresponding removal. Spawning N shells registers N listeners, all of which survive even after the shell is closed. After a few sessions this adds up. Store the listener and remove it when the shell is killed, or use the ResizeObserver pattern already used in TerminalManager.

Concern: clawcontainer added as a production dependency

package.json adds clawcontainer: ^1.1.0 to dependencies. The diff does not show any direct import of this package in the source files being changed. If it is only used internally by spawnShell or if it is an internal package, that needs to be documented. If it is unused, remove it to avoid unnecessary bundle bloat.

Concern: vite 5 → 8 major bump without documented rationale

The package.json bump from vite ^5.2.0 to vite ^8.0.1 is a two-major-version jump. Vite 8 requires Node >=20.19.0 || >=22.12.0 and switches from esbuild to rolldown. This changes the minimum runtime requirement for contributors and may affect the WebContainer boot environment. This should be explicitly called out in the PR description and ideally confirmed against the CI node version.

Minor: monaco-editor downgraded from ^0.55.1 to ^0.53.0

This is a regression — going backwards on a dependency is unusual and may re-introduce bugs that were fixed between those versions. The reason is not mentioned in the PR description. Please justify or revert.

Minor: CSS split-pane specificity

The rule #terminal-panes.split-active .term-pane.active { display: flex; } and #terminal-panes.split-active #terminal-pane-split { display: flex !important; } mixes a specificity-won selector with !important. Prefer consistent specificity throughout; the !important here is a code smell that will make future layout overrides harder.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants