Skip to content

Conversation

@ibetitsmike
Copy link
Contributor

@ibetitsmike ibetitsmike commented Feb 11, 2026

Summary

Adds a full-screen terminal UI (mux tui) built on Ink (React renderer for terminals) that connects to a running Mux server via oRPC. The TUI supports project/workspace navigation, creation, and interactive chat with streaming, tool calls, and ask_user_question support.

Background

Users working in terminal-only environments (SSH, remote servers, headless setups) need a way to interact with Mux without the desktop Electron app. The TUI reuses the existing oRPC API layer so all business logic stays centralized in the backend.

Implementation

Architecture

  • oRPC client model: TUI is a thin WebSocket client to a running Mux server (desktop app or mux server), same API surface as the Electron frontend
  • ESM bundle: dist/cli/tui.mjs built with esbuild, loaded via dynamic import() from the CJS CLI entry (mirrors mux api pattern)
  • Ink v5: Pinned to v5 for React 18 compatibility (Ink 6 requires React 19)

Core Features

  • Split-pane layout: persistent sidebar (30 chars) + main content panel with full box-drawing borders
  • Focus-aware borders: active panel highlighted in cyan, inactive in gray
  • Project/workspace navigation: arrow keys, Enter to select, n to create, Tab to switch panes
  • Chat: streaming assistant messages with markdown rendering (headers, lists, code blocks, links), tool call progress, ask_user_question interactive flow
  • Server discovery: MUX_SERVER_URL~/.mux/server.lock → fallback http://localhost:3000

Visual Design (Dark Theme)

  • Box-drawing section headers (─ Projects ───────)
  • Focus-aware panel borders (cyan active / gray inactive)
  • Styled code blocks with language labels
  • Soft color palette: #6fbf73 for user, #ce93d8 for assistant
  • Bottom status bar with keybinding hints

Anti-Flashing

  • Uses alternate screen buffer (\x1b[?1049h) so terminal double-buffers all Ink re-renders — no visible flashing on keypresses
  • Cursor hidden during TUI lifetime, restored on exit

File Layout

  • Entry: src/cli/tui.tsx
  • App root: src/cli/tui/TuiApp.tsx
  • State: src/cli/tui/tuiStore.ts, src/cli/tui/tuiTypes.ts
  • Components: FullScreenLayout, Sidebar, SelectableList, MarkdownText, CodeBlock, ToolCallBlock
  • Screens: ChatScreen, CreateProjectScreen, CreateWorkspaceScreen

Validation

  • make static-check passes (typecheck, lint, fmt, build, broken-links)
  • Manual smoke test: mux tui connects to running server, navigates projects/workspaces, sends/receives chat messages

Generated with mux • Model: anthropic:claude-opus-4-6 • Thinking: xhigh • Cost: $12.33

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 74044019d3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +365 to +367
dispatch({
type: "CHAT_ADD_MESSAGE",
message: { role: "user", content: messageText },

Choose a reason for hiding this comment

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

P1 Badge Remove duplicate optimistic user message append

This eagerly inserts the user message into local state before workspace.sendMessage, but the same user turn is also delivered through the active workspace.onChat stream (the backend emits user type: "message" events), so each successful send is rendered twice in the TUI transcript. This happens in the normal connected-chat flow and makes the conversation history inaccurate unless you dedupe by message ID or rely on the stream event only.

Useful? React with 👍 / 👎.

Comment on lines +263 to +264
if (isStreamError(event)) {
dispatch({ type: "SET_ERROR", error: event.error });

Choose a reason for hiding this comment

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

P2 Badge Reset stream state when stream-error arrives

On stream-error the code only sets a top-level error, leaving chat.isStreaming and streamingBuffer untouched. In terminal-error paths the backend can emit stream-error without a follow-up stream-end/stream-abort, so the TUI can remain stuck showing an active typing state and stale partial output even though the stream has already ended. Clear streaming state (same as abort/end) when handling this event.

Useful? React with 👍 / 👎.

branchName: trimmedBranchName,
trunkBranch: recommendedTrunk ?? undefined,
title: trimmedTitle.length > 0 ? trimmedTitle : undefined,
runtimeConfig: { type: "local" },

Choose a reason for hiding this comment

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

P1 Badge Stop forcing workspace creation to local runtime

Hardcoding runtimeConfig: { type: "local" } changes workspace creation semantics from the backend default and bypasses project/runtime expectations: local runtime does not create isolated worktrees and can also be rejected by policy in environments that only allow worktree/remote runtimes. This means mux tui workspace creation can silently produce incorrect workspace behavior or fail in policy-enforced setups.

Useful? React with 👍 / 👎.

Ink internally imports react-devtools-core. Marking it as --external
left a bare import in the ESM bundle that Node couldn't resolve at
runtime. Installing it as a devDependency and removing the --external
flag lets esbuild bundle it into dist/cli/tui.mjs.
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.

1 participant