From 896ab350b0ac37e09e715cf4d2b382d7eeb9fe8b Mon Sep 17 00:00:00 2001 From: tauraamui Date: Mon, 5 Jan 2026 23:24:26 +0000 Subject: [PATCH 1/4] feat: handle splash screen detecting left + right nav motions in tmux --- petal.v | 6 ++++++ splash_screen.v | 22 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/petal.v b/petal.v index 17936e08..3581d5b3 100644 --- a/petal.v +++ b/petal.v @@ -45,6 +45,12 @@ fn (mut m PetalModel) on_toggle_debug_screen() (tea.Model, ?tea.Cmd) { return m.clone(), none } +struct CheckIfTMUXWrappedMsg {} + +fn check_if_tmux_wrapped() tea.Msg { + return CheckIfTMUXWrappedMsg{} +} + fn (mut m PetalModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { mut cmds := []tea.Cmd{} match msg { diff --git a/splash_screen.v b/splash_screen.v index c9ce0f41..f0e910a8 100644 --- a/splash_screen.v +++ b/splash_screen.v @@ -1,6 +1,7 @@ module main import math +import os import tauraamui.bobatea as tea import palette @@ -21,6 +22,7 @@ struct SplashScreenModel { leader_key string logo SplashLogo mut: + tmux_wrapped bool leader_mode bool leader_data string dialog_model ?DebuggableModel @@ -36,7 +38,7 @@ fn SplashScreenModel.new() SplashScreenModel { } fn (mut m SplashScreenModel) init() ?tea.Cmd { - return none + return check_if_tmux_wrapped } fn (mut m SplashScreenModel) handle_escape() (tea.Model, ?tea.Cmd) { @@ -75,6 +77,9 @@ fn (mut m SplashScreenModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { } match msg { + CheckIfTMUXWrappedMsg { + m.tmux_wrapped = os.getenv("TMUX").len > 0 + } tea.KeyMsg { match msg.k_type { .special { @@ -85,6 +90,20 @@ fn (mut m SplashScreenModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { 'ctrl+c' { return m.handle_escape() } + 'ctrl+w+h' { + $if !darwin { + if m.tmux_wrapped { + os.execute('tmux select-pane -L') + } + } + } + 'ctrl+w+l' { + $if !darwin { + if m.tmux_wrapped { + os.execute('tmux select-pane -R') + } + } + } else {} } } @@ -351,6 +370,7 @@ fn (m SplashScreenModel) debug_data() DebugData { name: 'splash_screen data' data: { 'leader key': m.leader_key + 'tmux wrapped': '${m.tmux_wrapped}' '': if d := m.dialog_model { d.debug_data() } else { 'null' } 'version': '${version} - (${build_id})' } From 1cdde2309a17b9634c3d9ce123e879abf4f35247 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Mon, 5 Jan 2026 23:58:06 +0000 Subject: [PATCH 2/4] feat: editor workspace checks if wrapped --- editor_workspace.v | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/editor_workspace.v b/editor_workspace.v index 397009af..e83b7894 100644 --- a/editor_workspace.v +++ b/editor_workspace.v @@ -15,6 +15,7 @@ struct EditorWorkspaceModel { // the only way we can change the mode is by exiting the current scope with a command to do so mode Mode mut: + tmux_wrapped bool dialog_model ?DebuggableModel active_editor_id int @@ -59,7 +60,7 @@ fn EditorWorkspaceModel.new(initial_file_path string) EditorWorkspaceModel { fn (mut m EditorWorkspaceModel) init() ?tea.Cmd { m.input_field = boba.InputField.new_with_prefix(":", 0) - return tea.batch(open_editor(m.initial_file_path)) + return tea.batch(open_editor(m.initial_file_path), check_if_tmux_wrapped) } struct SwitchModeMsg { @@ -329,6 +330,10 @@ fn (mut m EditorWorkspaceModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { tea.FocusedMsg { cmds << query_pwd_git_branch } + CheckIfTMUXWrappedMsg { + m.tmux_wrapped = os.getenv("TMUX").len > 0 + assert m.tmux_wrapped + } OpenDialogMsg { mut d_model := msg.model cmd := d_model.init() From 6a432b2291d641aa596ec3e9a5b1f796a1fffaed Mon Sep 17 00:00:00 2001 From: tauraamui Date: Mon, 5 Jan 2026 23:58:26 +0000 Subject: [PATCH 3/4] feat: decide to wrap around or invoke tmux split nav --- editor_workspace.v | 57 ++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/editor_workspace.v b/editor_workspace.v index e83b7894..d9fda8a4 100644 --- a/editor_workspace.v +++ b/editor_workspace.v @@ -254,32 +254,49 @@ fn (mut m EditorWorkspaceModel) update(msg tea.Msg) (tea.Model, ?tea.Cmd) { // move to previous split (left) if m.split_tree.count() > 1 { old_id := m.split_tree.active_editor_id - m.split_tree.navigate_prev() - new_id := m.split_tree.active_editor_id - m.active_editor_id = new_id - - cmds << tea.sequence( - unfocus_editor(old_id), - focus_editor(new_id), - query_editor_data(new_id), - query_pwd_git_branch - ) + moved := m.split_tree.navigate_prev(m.tmux_wrapped) + + if moved == false { + os.execute('tmux select-pane -L') + } else { + new_id := m.split_tree.active_editor_id + m.active_editor_id = new_id + + cmds << tea.sequence( + unfocus_editor(old_id), + focus_editor(new_id), + query_editor_data(new_id), + query_pwd_git_branch + ) + } + } else { + if m.tmux_wrapped { + os.execute('tmux select-pane -L') + } } } "ctrl+w+l" { // move to next split (right) if m.split_tree.count() > 1 { old_id := m.split_tree.active_editor_id - m.split_tree.navigate_next() - new_id := m.split_tree.active_editor_id - m.active_editor_id = new_id - - cmds << tea.sequence( - unfocus_editor(old_id), - focus_editor(new_id), - query_editor_data(new_id), - query_pwd_git_branch - ) + moved := m.split_tree.navigate_next(m.tmux_wrapped) + if moved == false { + os.execute('tmux select-pane -R') + } else { + new_id := m.split_tree.active_editor_id + m.active_editor_id = new_id + + cmds << tea.sequence( + unfocus_editor(old_id), + focus_editor(new_id), + query_editor_data(new_id), + query_pwd_git_branch + ) + } + } else { + if m.tmux_wrapped { + os.execute('tmux select-pane -R') + } } } else {} From d440d5489a3115843be5360970bbb8be88300735 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Mon, 5 Jan 2026 23:58:43 +0000 Subject: [PATCH 4/4] chore: adjust navigation behaviour with flag --- split_tree.v | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/split_tree.v b/split_tree.v index 42b2100a..4eaff165 100644 --- a/split_tree.v +++ b/split_tree.v @@ -226,7 +226,7 @@ fn (t SplitTree) collect_editor_ids(node SplitNode) []int { } // Navigate to next editor -pub fn (mut t SplitTree) navigate_next() bool { +pub fn (mut t SplitTree) navigate_next(do_not_wrap_around bool) bool { all_ids := t.get_all_editor_ids() if all_ids.len <= 1 { return false @@ -238,13 +238,17 @@ pub fn (mut t SplitTree) navigate_next() bool { return true } - next_idx := (current_idx + 1) % all_ids.len + if do_not_wrap_around && current_idx == all_ids.len - 1 { + return false + } + + next_idx := if do_not_wrap_around { current_idx + 1 } else { (current_idx + 1) % all_ids.len } t.active_editor_id = all_ids[next_idx] return true } // Navigate to previous editor -pub fn (mut t SplitTree) navigate_prev() bool { +pub fn (mut t SplitTree) navigate_prev(do_not_wrap_around bool) bool { all_ids := t.get_all_editor_ids() if all_ids.len <= 1 { return false @@ -256,7 +260,11 @@ pub fn (mut t SplitTree) navigate_prev() bool { return true } - prev_idx := (current_idx - 1 + all_ids.len) % all_ids.len + if current_idx == 0 { + return false + } + + prev_idx := if do_not_wrap_around { current_idx - 1 } else { (current_idx - 1 + all_ids.len) % all_ids.len } t.active_editor_id = all_ids[prev_idx] return true } @@ -358,11 +366,11 @@ pub fn (mut t SplitTree) close_active_split() bool { if root := t.root { // navigate to next before closing - t.navigate_next() + t.navigate_next(false) // if we're still on the same ID after navigation, try previous if t.active_editor_id == old_active_id { - t.navigate_prev() + t.navigate_prev(false) } t.root = t.remove_editor_from_node(root, old_active_id)