Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
1fcc955
test(web): retain sizing across attach recovery
lawrencecchen Jul 17, 2026
90c04db
fix(web): preserve smallest sizing across recovery
lawrencecchen Jul 17, 2026
085731f
style(tui): satisfy detach resize lint
lawrencecchen Jul 17, 2026
9b0f783
fix(web): align byte fallback terminal font
lawrencecchen Jul 17, 2026
d3e90ac
test(tui): keep input independent from sizing
lawrencecchen Jul 17, 2026
b306078
fix(tui): remove sizing from input path
lawrencecchen Jul 17, 2026
74f4fbf
test(web): reject sizing while render stream is detached
lawrencecchen Jul 17, 2026
b1090b7
fix(web): gate sizing on active render stream
lawrencecchen Jul 17, 2026
2dbcd65
test(tui): wait for builtin sidebar reload
lawrencecchen Jul 17, 2026
9551a88
test(tui): wait for plugin removal before sidebar input
lawrencecchen Jul 17, 2026
87a6db0
fix(tui): preserve builtin sidebar focus
lawrencecchen Jul 17, 2026
1de2eb4
test(tui): cover viewer lifecycle sizing
lawrencecchen Jul 17, 2026
4af28fd
fix(tui): settle resize claims before recovery
lawrencecchen Jul 17, 2026
8d9e7c7
test(tui): require settled sidebar removal
lawrencecchen Jul 17, 2026
883a72a
test(tui): reproduce concurrent viewer resize race
lawrencecchen Jul 17, 2026
6ec2a1d
fix(tui): serialize shared viewer sizing
lawrencecchen Jul 17, 2026
fdc54e7
test(tui): reproduce early mutation settlement
lawrencecchen Jul 17, 2026
9fc3f77
fix(tui): make worker settlement authoritative
lawrencecchen Jul 17, 2026
16acb40
test(tui): scale PTY waits under valgrind
lawrencecchen Jul 17, 2026
6d69f89
Add connected client sizing controls
lawrencecchen Jul 17, 2026
ad18189
Track client sizing by visible tab
lawrencecchen Jul 17, 2026
c28ba22
Test self-disconnect menu lifecycle
lawrencecchen Jul 17, 2026
d59c9c4
Fix self-disconnect client lifecycle
lawrencecchen Jul 17, 2026
7b600d5
Stabilize sidebar plugin lifecycle test
lawrencecchen Jul 17, 2026
dd8f1c8
Allow sidebar lifecycle tests under load
lawrencecchen Jul 17, 2026
5ae8b3b
Keep TUI verification gates deterministic
lawrencecchen Jul 17, 2026
7f70403
Allow web resize recovery test under load
lawrencecchen Jul 17, 2026
bb2e8ac
Update web client sizing fixtures
lawrencecchen Jul 17, 2026
59d8ad5
Test tmux ignore-size fallback semantics
lawrencecchen Jul 17, 2026
72ab884
Match tmux ignore-size fallback semantics
lawrencecchen Jul 17, 2026
1918bc8
Test remaining tmux sizing lifecycle edges
lawrencecchen Jul 17, 2026
9eabed9
Close tmux sizing lifecycle gaps
lawrencecchen Jul 17, 2026
f28e353
Test tmux sizing bounds and fallback labels
lawrencecchen Jul 17, 2026
4f93261
Clamp and label tmux shared sizes correctly
lawrencecchen Jul 17, 2026
2d8f8cb
Apply client sizing modes atomically
lawrencecchen Jul 17, 2026
e965f87
Add per-pane client controls to web frontend
lawrencecchen Jul 17, 2026
5856c21
Test unattached resize against shared minimum
lawrencecchen Jul 17, 2026
8b4594b
Route all resizes through shared client sizing
lawrencecchen Jul 17, 2026
a7819fd
Settle exclusive sizing through disconnect races
lawrencecchen Jul 17, 2026
a3621be
Reject stale local sizing targets
lawrencecchen Jul 17, 2026
8bfe1db
Separate explicit and viewer-derived size defaults
lawrencecchen Jul 17, 2026
96323ac
Keep nested client menus inside viewport
lawrencecchen Jul 17, 2026
7aa9820
Merge remote-tracking branch 'origin/main' into fix-tui-smallest-reco…
lawrencecchen Jul 17, 2026
befda7b
fix(tui): harden multi-client sizing lifecycle
lawrencecchen Jul 18, 2026
5547c5b
fix(tui): close multi-client review gaps
lawrencecchen Jul 18, 2026
f7ca8ed
fix(tui): harden nested menu recovery
lawrencecchen Jul 18, 2026
e5c3d33
fix(tui): refresh clients after remote recovery
lawrencecchen Jul 18, 2026
0fd9243
fix(tui): coalesce remote subscription recovery
lawrencecchen Jul 18, 2026
233eb06
fix(tui): linearize subscription recovery
lawrencecchen Jul 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/cmux-tui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ jobs:
# otherwise-correct build; the guarded regression (events serialized
# behind a 100ms read poll) inflates far past this bound anyway.
CMUX_TEST_WS_LATENCY_BUDGET_MS: "2000"
# Process-exit and PTY-reader tests also use bounded polling. Keep
# their normal deadlines strict while allowing for instrumentation.
CMUX_TEST_TIMEOUT_SCALE: "4"
run: |
while IFS= read -r bin; do
[ -n "$bin" ] || continue
Expand Down
4 changes: 4 additions & 0 deletions cmux-tui/bindings/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,10 @@ impl CmuxClient {
self.request("resize-surface", params)
}

pub fn release_surface_size(&mut self, surface: u64) -> Result<()> {
self.request::<Empty>("release-surface-size", surface_params(surface)).map(|_| ())
}

pub fn focus_pane(&mut self, pane: u64) -> Result<()> {
let mut params = Map::new();
params.insert("pane".to_string(), Value::from(pane));
Expand Down
12 changes: 12 additions & 0 deletions cmux-tui/bindings/typescript/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,15 @@ export class CmuxClient {
}
listClients(): Promise<ListClientsResult> { return this.request("list-clients"); }
detachClient(client: Id): Promise<EmptyResult> { return this.request("detach-client", { client }); }
setClientSizing(client: Id, enabled: boolean): Promise<EmptyResult> {
return this.request("set-client-sizing", { client, enabled });
}
useOnlyClientSizing(client: Id): Promise<EmptyResult> {
return this.request("set-client-sizing", { client, enabled: true, exclusive: true });
}
useAllClientSizing(): Promise<EmptyResult> {
return this.request("set-client-sizing", { enabled: true });
}
reloadConfig(): Promise<ReloadConfigResult> { return this.request("reload-config"); }
setWindowTitle(title: string): Promise<EmptyResult> { return this.request("set-window-title", { title }); }
clearWindowTitle(): Promise<EmptyResult> { return this.request("clear-window-title"); }
Expand Down Expand Up @@ -464,6 +473,9 @@ export class CmuxClient {
const result = await this.request("resize-surface", { surface, cols, rows });
return { ...result, accepted: result.accepted ?? true };
}
releaseSurfaceSize(surface: Id): Promise<EmptyResult> {
return this.request("release-surface-size", { surface });
}
focusPane(pane: Id): Promise<EmptyResult> { return this.request("focus-pane", { pane }); }
selectTab(options: SelectTabOptions = {}): Promise<EmptyResult> { return this.request("select-tab", options); }
selectScreen(options: SelectOptions = {}): Promise<EmptyResult> { return this.request("select-screen", options); }
Expand Down
17 changes: 16 additions & 1 deletion cmux-tui/bindings/typescript/src/protocol/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export interface SetClientInfoRequest extends CmuxRequestBase {
}

export interface ListClientsRequest extends CmuxRequestBase { cmd: "list-clients" }
export type ClientTransport = "unix" | "ws";
export type ClientTransport = "local" | "unix" | "ws";
export interface ClientSize {
surface: Id;
cols: number | null;
Expand All @@ -43,10 +43,17 @@ export interface ClientInfo {
attached: Id[];
sizes: ClientSize[];
self: boolean;
size_participating: boolean;
}
export type ListClientsResult = ClientInfo[];

export interface DetachClientRequest extends CmuxRequestBase { cmd: "detach-client"; client: Id }
export interface SetClientSizingRequest extends CmuxRequestBase {
cmd: "set-client-sizing";
client?: Id;
enabled: boolean;
exclusive?: boolean;
}

export interface ReloadConfigRequest extends CmuxRequestBase { cmd: "reload-config" }
export interface ReloadConfigResult { reloaded: true; path: string | null }
Expand Down Expand Up @@ -209,6 +216,10 @@ export interface ResizeSurfaceRequest extends CmuxRequestBase {
rows: number;
}
export interface ResizeSurfaceResult { accepted: boolean; reservation_id?: number | null }
export interface ReleaseSurfaceSizeRequest extends CmuxRequestBase {
cmd: "release-surface-size";
surface: Id;
}

export interface FocusPaneRequest extends CmuxRequestBase { cmd: "focus-pane"; pane: Id }

Expand Down Expand Up @@ -321,6 +332,7 @@ export type CmuxRequest =
| SetClientInfoRequest
| ListClientsRequest
| DetachClientRequest
| SetClientSizingRequest
| ReloadConfigRequest
| SetWindowTitleRequest
| ClearWindowTitleRequest
Expand Down Expand Up @@ -353,6 +365,7 @@ export type CmuxRequest =
| RenameScreenRequest
| RenameWorkspaceRequest
| ResizeSurfaceRequest
| ReleaseSurfaceSizeRequest
| FocusPaneRequest
| SelectTabRequest
| SelectScreenRequest
Expand All @@ -378,6 +391,7 @@ export interface CmuxResponseDataMap {
"set-client-info": EmptyResult;
"list-clients": ListClientsResult;
"detach-client": EmptyResult;
"set-client-sizing": EmptyResult;
"reload-config": ReloadConfigResult;
"set-window-title": EmptyResult;
"clear-window-title": EmptyResult;
Expand Down Expand Up @@ -410,6 +424,7 @@ export interface CmuxResponseDataMap {
"rename-screen": EmptyResult;
"rename-workspace": EmptyResult;
"resize-surface": ResizeSurfaceResult;
"release-surface-size": EmptyResult;
"focus-pane": EmptyResult;
"select-tab": EmptyResult;
"select-screen": EmptyResult;
Expand Down
3 changes: 2 additions & 1 deletion cmux-tui/bindings/typescript/src/protocol/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,8 @@ export type KnownRenderAttachEvent =
| RenderStateEvent
| RenderDeltaEvent
| ScrollChangedEvent
| DetachedEvent;
| DetachedEvent
| OverflowEvent;

/** Render attachment events, including unknown future event names. */
export type RenderAttachEvent = KnownRenderAttachEvent | UnknownEvent;
34 changes: 34 additions & 0 deletions cmux-tui/bindings/typescript/test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@ test("listClients returns the exact client presence response shape", async () =>
attached: [31],
sizes: [{ surface: 31, cols: 126, rows: 38 }],
self: true,
size_participating: true,
}];
const transport = new ScriptedTransport((request, connection) => {
assert.deepEqual(request, { id: 1, cmd: "list-clients" });
Expand All @@ -448,6 +449,39 @@ test("listClients returns the exact client presence response shape", async () =>
await client.close();
});

test("setClientSizing serializes client participation", async () => {
const transport = new ScriptedTransport((request, connection) => {
assert.deepEqual(request, {
id: 1,
cmd: "set-client-sizing",
client: 7,
enabled: false,
});
connection.emit({ id: request.id, ok: true, data: {} });
});
const client = new CmuxClient({ transport });

await client.setClientSizing(7, false);
await client.close();
});

test("client sizing modes serialize as one atomic command", async () => {
const expected = [
{ id: 1, cmd: "set-client-sizing", client: 7, enabled: true, exclusive: true },
{ id: 2, cmd: "set-client-sizing", enabled: true },
];
const transport = new ScriptedTransport((request, connection) => {
assert.deepEqual(request, expected.shift());
connection.emit({ id: request.id, ok: true, data: {} });
});
const client = new CmuxClient({ transport });

await client.useOnlyClientSizing(7);
await client.useAllClientSizing();
assert.equal(expected.length, 0);
await client.close();
});

test("readScrollback serializes the request and returns styled rows", async () => {
const response = {
rows: [{ row: 0, runs: [{ text: "cargo test", fg: null, bg: null, attrs: 0 }] }],
Expand Down
2 changes: 2 additions & 0 deletions cmux-tui/bindings/typescript/test/protocol-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const requests = [
{ cmd: "set-client-info", name: "browser", kind: "web" },
{ cmd: "list-clients" },
{ cmd: "detach-client", client: 2 },
{ cmd: "set-client-sizing", client: 2, enabled: false },
{ cmd: "reload-config" },
{ cmd: "set-window-title", title: "cmux" },
{ cmd: "clear-window-title" },
Expand Down Expand Up @@ -51,6 +52,7 @@ const requests = [
{ cmd: "rename-screen", screen: 1, name: "screen" },
{ cmd: "rename-workspace", workspace: 1, name: "workspace" },
{ cmd: "resize-surface", surface: 1, cols: 80, rows: 24 },
{ cmd: "release-surface-size", surface: 1 },
{ cmd: "focus-pane", pane: 1 },
{ cmd: "select-tab", pane: 1, index: 0 },
{ cmd: "select-screen", delta: 1 },
Expand Down
Loading