fix(bot-mode): bound the Bots home re-front so the view stops strobing

Re-fronting the Bots home tab is a close followed by a re-open, which
tears down and rebuilds the entire Bots view. `openBotsHomeWorkspace`
took that path on EVERY passive reconcile that found the tab open but
not holding its zone's active slot, with nothing bounding the retries.

That condition is not always transient. `revealTreePane` returns early
for a pane in `$hiddenTreePanes` without ever activating it,
`isPaneVisible` is false for a minimized zone, and a pane the tree
never adopted has no group to be active in. Pinned in any of those
states, every signal that reaches a surface sync — sidebar visibility
flips, focus churn, group changes — bought one more full remount, and
the view visibly strobed.

A passive reconcile now gets one attempt. The reveal has already
granted or refused the active slot by the time `openWorkspace` returns,
so the budget settles on that answer directly instead of waiting for a
visibility notification that is not coming: a computed store stays
silent when the value does not change. Retiring the tab starts a fresh
budget, and an explicit gesture is never blocked.

Giving up keeps the surface rather than closing it — a closed home
drops the Bots tab through to the ownerless Sessions composer, which is
the hole the home exists to plug.
This commit is contained in:
HexLab98
2026-08-23 22:49:57 +07:00
committed by Teknium
parent 127a72b223
commit 2350f4dc60
@@ -316,6 +316,10 @@ const $botsHomeFronted = atom(false)
let botsHomeClose = null
let suppressBotsHomeReopen = false
// Latched while a re-front attempt has not yet been answered with visibility.
// Cleared the moment the home is actually fronted, and whenever the tab is
// retired — a fresh open starts the budget over. See openBotsHomeWorkspace.
let botsHomeRefrontTried = false
function saveSelectedRosterBot(bot) {
const key = botRosterKey(bot)
@@ -13122,6 +13126,10 @@ function closeBotsHomeWorkspace() {
const close = botsHomeClose
botsHomeClose = null
suppressBotsHomeReopen = true
// Retiring the tab ends the current attempt's budget: the next open is a
// new surface, and it gets its own re-front chance. The re-front path sets
// the latch again AFTER calling us, so this cannot erase its own attempt.
botsHomeRefrontTried = false
try {
close()
@@ -13209,10 +13217,27 @@ function openBotsHomeWorkspace(explicit = false) {
// and each of those either legitimately claims the center or cleared it.
if (botsHomeClose) {
if (botsHomeVisible()) {
botsHomeRefrontTried = false
return true
}
// A re-front is a close + re-open, so it REMOUNTS the whole Bots view.
// That is affordable once, against the backgrounded-tab case above. It is
// not affordable per signal: the shell does not always answer a reveal
// with the active slot (revealTreePane returns early for a pane in
// $hiddenTreePanes without activating it; a minimized zone and a pane the
// tree never adopted are never visible either). Pinned in one of those
// states the old code re-fronted on every passive pass — sidebar flips,
// focus churn and group changes all reach here — and the view strobed.
// One attempt proves whether this shell will front the tab; if it will
// not, keep the surface and wait for a signal that changes the answer.
if (botsHomeRefrontTried && !explicit) {
return true
}
closeBotsHomeWorkspace()
botsHomeRefrontTried = true
}
try {
@@ -13230,6 +13255,15 @@ function openBotsHomeWorkspace(explicit = false) {
}
})
// The reveal has already either granted the tab its zone's active slot or
// refused it, so settle the re-front budget on that answer directly rather
// than waiting for a visibility notification to schedule another pass. A
// computed store stays silent when the value does not change, so on the
// shells that refuse, no such pass is coming.
if (botsHomeVisible()) {
botsHomeRefrontTried = false
}
return typeof botsHomeClose === 'function'
} catch {
botsHomeClose = null