Skip to content

Sidebar ports on own line, wider sidebar, CMUX_PORT env vars - #160

Merged
lawrencecchen merged 6 commits into
mainfrom
feat-sidebar-ports
Feb 20, 2026
Merged

lawrencecchen merged 6 commits into
mainfrom
feat-sidebar-ports

Conversation

@lawrencecchen

Copy link
Copy Markdown
Contributor

Summary

  • Move listening ports to a dedicated sidebar row below branch/directory line
  • Allow sidebar to resize up to 2/3 of screen width (was capped at 360px)
  • Add CMUX_PORT, CMUX_PORT_END, CMUX_PORT_RANGE env vars per workspace with configurable base port and range size in settings

Test plan

  • Run python3 -m http.server 8000 — ports show on own line without globe icon
  • Drag sidebar wider — goes past old 360px cap
  • Run echo $CMUX_PORT $CMUX_PORT_END — first workspace shows 9100 9109
  • Open second workspace, same command — shows 9110 9119
  • Check settings (Cmd+,) for Port Base and Port Range Size fields

Closes #129

- Move listening ports to dedicated sidebar row (removed from branch/directory line)
- Allow sidebar to resize up to 2/3 of screen width (was capped at 360px)
- Add CMUX_PORT, CMUX_PORT_END, CMUX_PORT_RANGE env vars per workspace
- Each workspace gets a dedicated port range (default: base 9100, range 10)
- Add settings UI for port base and range size
- Add portOrdinal to Workspace, monotonic counter in TabManager

Closes #129
@cmux-agent

cmux-agent Bot commented Feb 20, 2026 •

Copy link
Copy Markdown
Contributor
Older manaflow preview screenshots (latest comment is below)

Preview Videos and Screenshots

Open Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap

Screenshot capture was skipped.

No UI changes detected - screenshots skipped


Generated by manaflow preview system

@vercel

vercel Bot commented Feb 20, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Feb 20, 2026 10:09pm

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

Copy link
Copy Markdown

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: c39704c0d0

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/TabManager.swift Outdated
Comment on lines +400 to +401
let newWorkspace = Workspace(title: "Terminal \(tabs.count + 1)", workingDirectory: workingDirectory)
newWorkspace.portOrdinal = nextPortOrdinal

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Assign workspace port ordinal before constructing workspace

addWorkspace sets newWorkspace.portOrdinal only after Workspace(...) returns, but Workspace.init immediately creates its initial TerminalPanel using the current portOrdinal value (Sources/Workspace.swift:141-146), so every new workspace’s first terminal is started with ordinal 0 and gets the wrong CMUX_PORT range; this is user-visible as soon as a second workspace is opened and echo $CMUX_PORT is run in its initial terminal.

Useful? React with 👍 / 👎.

Comment thread Sources/TabManager.swift Outdated
Comment on lines +230 to +231
/// Monotonically increasing counter for CMUX_PORT ordinal assignment
private var nextPortOrdinal: Int = 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Allocate port ordinals globally instead of per TabManager

The ordinal counter is instance state on TabManager, but the app creates a new TabManager per window (Sources/AppDelegate.swift:682-685), so opening another window resets the counter and reuses the same CMUX_PORT ranges (for example, both windows’ first workspaces get 9100-9109), which defeats the “dedicated port range per workspace” behavior whenever users run services in multiple windows.

Useful? React with 👍 / 👎.

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Re: port ordinal counter concern — not applicable. There's only a single TabManager instance in the app, created as a @StateObject on the cmuxApp struct (cmuxApp.swift:7). It lives for the entire app lifecycle. There is no per-window TabManager.

Each window creates its own TabManager, so a per-instance counter
would reset and reuse port ranges. Making it static ensures unique
ranges across all windows.
@cmux-agent

cmux-agent Bot commented Feb 20, 2026 •

Copy link
Copy Markdown
Contributor
Older manaflow preview screenshots (latest comment is below)

Preview Videos and Screenshots

Open Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap

Screenshot capture was skipped.

No UI changes detected - screenshots skipped


Generated by manaflow preview system

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Correction: Codex was right. AppDelegate.createMainWindow() (line 684) creates a new TabManager per window. Fixed in e3bf6a0 — made nextPortOrdinal static so it's shared across all TabManager instances.

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

@codex review

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

Copy link
Copy Markdown

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: e3bf6a0ea6

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/TabManager.swift Outdated
Comment on lines +401 to +402
let newWorkspace = Workspace(title: "Terminal \(tabs.count + 1)", workingDirectory: workingDirectory)
newWorkspace.portOrdinal = Self.nextPortOrdinal

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Pass port ordinal into workspace initialization

addWorkspace sets newWorkspace.portOrdinal only after Workspace(...) returns, but Workspace.init immediately creates the initial TerminalPanel/TerminalSurface using the default ordinal 0. That means the first terminal in each new workspace can receive the same CMUX_PORT range as workspace 1, so opening a second workspace and running echo $CMUX_PORT $CMUX_PORT_END may still show the first range; only panels created later in that workspace inherit the updated ordinal.

Useful? React with 👍 / 👎.

…after

The first TerminalPanel is created inside Workspace.init, so setting
portOrdinal after init returns meant the initial terminal always got
ordinal 0. Pass portOrdinal as an init parameter and set it before
the TerminalPanel is created.
@cmux-agent

cmux-agent Bot commented Feb 20, 2026 •

Copy link
Copy Markdown
Contributor
Older manaflow preview screenshots (latest comment is below)

Preview Videos and Screenshots

Open Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap

Screenshot capture was skipped.

No UI changes detected - screenshots skipped


Generated by manaflow preview system

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

@codex review

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

Copy link
Copy Markdown

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: 91cad20f61

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/GhosttyTerminalView.swift Outdated
Comment on lines +1253 to +1257
let portBase = UserDefaults.standard.integer(forKey: "cmuxPortBase")
let portRange = UserDefaults.standard.integer(forKey: "cmuxPortRange")
let effectiveBase = portBase > 0 ? portBase : 9100
let effectiveRange = portRange > 0 ? portRange : 10
let startPort = effectiveBase + portOrdinal * effectiveRange

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Freeze workspace port range when terminals are spawned

TerminalSurface.createSurface recomputes CMUX_PORT/CMUX_PORT_END from live UserDefaults each time a panel is created, so changing cmuxPortBase or cmuxPortRange mid-session causes new terminals in an existing workspace to get different values than terminals already in that same workspace. This can also reintroduce overlap across workspaces (e.g., after increasing range size), which breaks the “dedicated range per workspace” behavior.

Useful? React with 👍 / 👎.

Comment thread Sources/ContentView.swift Outdated
}
}
let nextWidth = max(186, min(360, value.location.x - sidebarMinX + sidebarHandleWidth / 2))
let maxSidebarWidth = (NSScreen.main?.frame.width ?? 1920) * 2 / 3

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Derive sidebar width cap from the active display

The new resize cap uses NSScreen.main, which is the primary display rather than the display containing this window. On multi-monitor setups this makes the sidebar cap incorrect for windows on non-primary screens (too narrow on larger secondary displays, or overly permissive on smaller ones), so the “2/3 of screen width” rule is not applied to the actual screen the user is working on.

Useful? React with 👍 / 👎.

…een for sidebar cap

P2: Port base/range are now snapshotted on TerminalSurface when the
panel is created, so changing settings mid-session won't cause
inconsistent CMUX_PORT values across terminals in the same workspace.

P3: Sidebar max width now uses NSApp.keyWindow?.screen instead of
NSScreen.main, so multi-monitor setups get the correct 2/3 cap for
the display the window is actually on.
@cmux-agent

cmux-agent Bot commented Feb 20, 2026 •

Copy link
Copy Markdown
Contributor
Older manaflow preview screenshots (latest comment is below)

Preview Videos and Screenshots

Open Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap

Screenshot status is unknown for this run.


Generated by manaflow preview system

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

@codex review

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

Copy link
Copy Markdown

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: f8e645525b

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/Panels/TerminalPanel.swift Outdated
Comment on lines +95 to +96
surface.portBase = UserDefaults.standard.integer(forKey: "cmuxPortBase")
surface.portRangeSize = UserDefaults.standard.integer(forKey: "cmuxPortRange")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep port range immutable for ordinal mapping

Reading cmuxPortBase/cmuxPortRange from UserDefaults for each new panel makes the startPort = base + portOrdinal * range calculation in TerminalSurface.createSurface depend on mutable settings, so changing range/base mid-session can assign overlapping ranges to different workspaces (for example, workspace ordinal 1 created with range 10 uses 9110-9119, then after switching range to 5, ordinal 2 starts at 9110). This breaks the dedicated non-overlapping workspace port contract and can cause cross-workspace port conflicts.

Useful? React with 👍 / 👎.

Port base and range size are now static properties on TerminalSurface,
initialized once from UserDefaults at first access. This prevents
overlapping port ranges across workspaces when settings are changed
mid-session (e.g., workspace 1 with range=10 at 9110-9119, then
range changed to 5, workspace 2 would overlap at 9110).
@cmux-agent

cmux-agent Bot commented Feb 20, 2026 •

Copy link
Copy Markdown
Contributor
Older manaflow preview screenshots (latest comment is below)

Preview Videos and Screenshots

Open Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap

Screenshot capture was skipped.

No UI changes detected - screenshots skipped


Generated by manaflow preview system

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Resolve conflict in Workspace.swift: keep portOrdinal init parameter
alongside new bonsplit chrome color methods from main.
@cmux-agent

cmux-agent Bot commented Feb 20, 2026 •

Copy link
Copy Markdown
Contributor

Preview Videos and Screenshots

Open Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap

Screenshot capture was skipped.

No UI changes detected - screenshots skipped


Generated by manaflow preview system

@lawrencecchen
lawrencecchen merged commit cae40d1 into main Feb 20, 2026
5 checks passed
@lawrencecchen
lawrencecchen deleted the feat-sidebar-ports branch February 20, 2026 22:12
bn-l pushed a commit to bn-l/cmux that referenced this pull request Apr 3, 2026
…w-ai#160)

* Sidebar ports on own line, wider sidebar, CMUX_PORT env vars

- Move listening ports to dedicated sidebar row (removed from branch/directory line)
- Allow sidebar to resize up to 2/3 of screen width (was capped at 360px)
- Add CMUX_PORT, CMUX_PORT_END, CMUX_PORT_RANGE env vars per workspace
- Each workspace gets a dedicated port range (default: base 9100, range 10)
- Add settings UI for port base and range size
- Add portOrdinal to Workspace, monotonic counter in TabManager

Closes manaflow-ai#129

* Make port ordinal counter static to avoid overlap across windows

Each window creates its own TabManager, so a per-instance counter
would reset and reuse port ranges. Making it static ensures unique
ranges across all windows.

* Fix portOrdinal race: pass through Workspace init instead of setting after

The first TerminalPanel is created inside Workspace.init, so setting
portOrdinal after init returns meant the initial terminal always got
ordinal 0. Pass portOrdinal as an init parameter and set it before
the TerminalPanel is created.

* Fix P2/P3: snapshot port settings at surface creation, use window screen for sidebar cap

P2: Port base/range are now snapshotted on TerminalSurface when the
panel is created, so changing settings mid-session won't cause
inconsistent CMUX_PORT values across terminals in the same workspace.

P3: Sidebar max width now uses NSApp.keyWindow?.screen instead of
NSScreen.main, so multi-monitor setups get the correct 2/3 cap for
the display the window is actually on.

* Fix P1: snapshot port base/range once per app session, not per panel

Port base and range size are now static properties on TerminalSurface,
initialized once from UserDefaults at first access. This prevents
overlapping port ranges across workspaces when settings are changed
mid-session (e.g., workspace 1 with range=10 at 9110-9119, then
range changed to 5, workspace 2 would overlap at 9110).
@coderabbitai coderabbitai Bot mentioned this pull request Sep 22, 2026
3 of 6 tasks

This branch was successfully deployed

1 active deployment
Preview — cf890d5d Deployed Feb 20, 2026 by vercel[bot]
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.

Sidebar ports on own line, wider sidebar, CMUX_PORT env var

1 participant