From 3dbc1b11dff04c8873ad59daf11e89dd365bc1d0 Mon Sep 17 00:00:00 2001 From: en Date: Wed, 16 Sep 2026 08:03:27 +0200 Subject: [PATCH] swap_as_main: keep slot IDs stable, set_leader(n) (Bug #11) Bug from Grok round-1 #11. swap_as_main used to renumber slot IDs to 1..=N after a vec swap, which silently rebroke every per-character assist/follow key (the user typed Alt+1 in slot 1 to assist that character; after the swap, slot 1 contained a different character). Fix: do not renumber. Slot ID continues to identify a character; vec position only encodes which physical tile the window occupies. set_leader is called with n (the user's chosen new main). The notify message now reports both the new and previous main using the actual slot IDs. cargo test 96+/0; clippy clean. --- src/session.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/session.rs b/src/session.rs index ba4f5f7..b57a67a 100644 --- a/src/session.rs +++ b/src/session.rs @@ -624,17 +624,23 @@ async fn swap_prev(session: &Arc>) -> Result<()> { } async fn swap_as_main(session: &Arc>, n: u32) -> Result<()> { - let (wins, tiles) = { + let (wins, tiles, prev_leader) = { let mut g = session.lock().await; if n < 2 || n as usize > g.slots.len() { return Ok(()); } + // Swap vec positions so the new leader's window sits at index 0 + // (the "main" tile for layout purposes). Do NOT renumber slot + // IDs: a slot ID identifies a character (and therefore a + // per-character assist/follow key), while vec position only + // identifies which physical tile the window currently occupies. + // Mixing the two was Bug #11 -- swapping then renumbering made + // every per-character macro target a different character than + // before. g.slots.swap(0, n as usize - 1); - for (i, (id, _)) in g.slots.iter_mut().enumerate() { - *id = i as u32 + 1; - } - g.engine.set_leader(1); - (g.slots.clone(), g.engine.profile.layout.slots.clone()) + let prev = g.engine.leader_slot; + g.engine.set_leader(n); + (g.slots.clone(), g.engine.profile.layout.slots.clone(), prev) }; if !tiles.is_empty() { crate::layout::apply(&tiles, &wins).await?; @@ -642,9 +648,11 @@ async fn swap_as_main(session: &Arc>, n: u32) -> Result<()> { if let Some((_, c)) = wins.first() { hypr::focus_window(&c.address_selector()).await.ok(); } - hypr::notify(&format!("main is slot 1 (was {n})")) - .await - .ok(); + hypr::notify(&format!( + "main is slot {n} (was {prev_leader})" + )) + .await + .ok(); Ok(()) }