Skip to content

fix: keep tab titles stable across pane focus#186

Open
W25X80 wants to merge 3 commits intosemos-labs:mainfrom
W25X80:fix/tab-rename-stable-title
Open

fix: keep tab titles stable across pane focus#186
W25X80 wants to merge 3 commits intosemos-labs:mainfrom
W25X80:fix/tab-rename-stable-title

Conversation

@W25X80
Copy link
Copy Markdown
Contributor

@W25X80 W25X80 commented Mar 29, 2026

tab rename currently behaves like renaming the focused pane, so a renamed tab can change its label when focus moves between panes inside the same tab.

This stores explicit titles on the tab itself and keeps pane-derived titles only as fallback for unnamed tabs. As a result, renamed tabs stay stable across pane focus changes, tab moves, and session restore.

Use case: rename a split tab to editor, then switch focus between vim, shell, and logs panes inside that tab. The tab should keep showing editor instead of following the currently focused pane title.

W25X80 added 2 commits March 29, 2026 22:53
Reject empty tab rename payloads across IPC and palette flows.

Store explicit tab title flags in a legacy-compatible trailer so older layout readers can still consume the base body without losing structural state.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes tab renaming so a renamed tab’s label remains stable when focus moves between panes, by storing titles on the tab/layout itself and using pane-derived titles only as fallback (including across session serialization/restore).

Changes:

  • Move “tab rename” semantics from per-pane custom titles to per-tab stored titles (SplitLayout.setTitle/getTitle) with fallback hint titles for unnamed tabs.
  • Extend the layout codec to persist explicit-vs-hint title semantics via an optional trailer while keeping the legacy body readable.
  • Update IPC handlers / query output and overlay/UI code to read/write the new tab-title sources, and add targeted tests.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/overlay/command_palette.zig Prevents rename submission on empty input; adds tests for rename mode behavior.
src/ipc/tab_rename.zig Adds shared payload parsing for active/targeted tab rename IPC messages.
src/ipc/handler_windows.zig Routes tab rename to SplitLayout titles; updates tab/pane listing title resolution.
src/ipc/handler_query.zig Updates list/tab/split query output to prefer explicit tab titles with fallbacks.
src/ipc/handler_cmd.zig Updates targeted tab rename handler to set tab title and persist session layout.
src/ipc/handler.zig Updates active tab rename handler to set tab title and persist session layout.
src/config/cli_ipc_help.zig Documents that tab rename titles must be non-empty.
src/app/ui/win_overlays.zig Updates command palette rename action to set explicit tab title + persist on Windows.
src/app/ui/publish.zig Updates tab title resolution chain to prefer explicit tab titles, then pane-derived fallbacks.
src/app/ui/event_loop_windows.zig Updates Windows tab title resolution; exposes cleanWindowsTitle for reuse.
src/app/ui/command_palette_ui.zig Updates command palette rename action to set explicit tab title + persist.
src/app/tab_manager_test.zig Adds tests covering explicit title persistence across serialize/restore/move/focus/sync.
src/app/tab_manager.zig Persists explicit/hint titles into layout blobs and restores them into SplitLayout.
src/app/split_layout.zig Adds stored title + hint title buffers and accessors on the tab layout object.
src/app/pane.zig Removes per-pane custom title fields/APIs (tab rename is no longer pane-scoped).
src/app/layout_codec.zig Adds title_flags + optional trailer encoding to preserve explicit-vs-hint semantics.
src/app/daemon/session_restore_test.zig Updates restore tests to assert explicit title flags survive detach/reattach.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 52 to +62
@@ -42,6 +56,13 @@ pub const LayoutInfo = struct {
tabs: [max_tabs]TabLayout = undefined,
};

fn hasTitleTrailer(info: *const LayoutInfo) bool {
for (0..info.tab_count) |ti| {
if (info.tabs[ti].title_flags != 0) return true;
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

LayoutInfo.tabs is declared as undefined, but serialize() calls hasTitleTrailer() which reads info.tabs[ti].title_flags for every tab. Any caller that builds var info = LayoutInfo{} and only fills node fields (several tests in this file do) will leave title_flags uninitialized, leading to undefined behavior and nondeterministic emission of the ttl2 trailer. Initialize tabs with a default value (e.g., .{.{}} ** max_tabs) or otherwise guarantee title_flags is zeroed for all tabs before serialize() reads it.

Copilot uses AI. Check for mistakes.
Comment on lines 326 to 330
if (idx == 0 or idx > ctx.tab_mgr.count) {
sendError(cmd, "invalid tab index");
return;
}
ctx.tab_mgr.closeTab(idx - 1);
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

handleTabCloseTargeted interprets the payload index as 1-based (idx == 0 invalid, closeTab(idx - 1)), but the IPC protocol/client and the POSIX handler treat tab_close_targeted as 0-based (client sends n-1, and handler_cmd.handleTabCloseTargeted closes closeTab(ti)). On Windows this makes attyx tab close N close the wrong tab (or reject tab 1 as idx=0). Update this handler to use 0-based indexing to match the protocol and other platforms.

Suggested change
if (idx == 0 or idx > ctx.tab_mgr.count) {
sendError(cmd, "invalid tab index");
return;
}
ctx.tab_mgr.closeTab(idx - 1);
// `idx` is 0-based (matches IPC protocol). Validate bounds accordingly.
if (idx >= ctx.tab_mgr.count) {
sendError(cmd, "invalid tab index");
return;
}
ctx.tab_mgr.closeTab(idx);

Copilot uses AI. Check for mistakes.
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