fix(bot-mode): group chat opens as one room pane, not two (#89788)
Opening a Bot Mode group chat painted the room twice — once as a main-window workspace tab (host.openWorkspace) and once as the in-panel fallback, because the Bots pane rendered off $groupChatWorkspace alone. Two live panes with independent drafts drove one shared engine, and the roster disappeared behind the duplicate. The in-panel room is the fallback surface, not a second copy: it now renders only while no main tab owns the group. The selection atom stays set either way so the roster row still highlights, and desktops without the door — or whose door throws — keep the in-pane room. Consolidates #89881, #90274 and #90398, which fixed the same bug. Closes #89788 Co-authored-by: helix4u <helix4u@users.noreply.github.com>
This commit is contained in:
@@ -9448,6 +9448,16 @@ function GroupChatWorkspace({ group, members, onBack }) {
|
||||
* disband (or the room view's own Back) can retire the tab it opened. */
|
||||
const groupChatMainTabs = new Map()
|
||||
|
||||
/** The in-panel room is the FALLBACK surface, not a second copy: it renders
|
||||
* only while no main-window tab owns the group. On desktops with the door
|
||||
* the room already lives in a main tab, and painting it here too produced
|
||||
* two live panes with independent drafts driving one shared engine (#89788).
|
||||
* The selection atom stays set either way so the roster row still
|
||||
* highlights. */
|
||||
function shouldRenderGroupChatInPane(group) {
|
||||
return Boolean(group && !groupChatMainTabs.has(group))
|
||||
}
|
||||
|
||||
function closeGroupChatMainTab(group) {
|
||||
const close = groupChatMainTabs.get(group)
|
||||
|
||||
@@ -9737,7 +9747,7 @@ function BotsPane() {
|
||||
|
||||
const groupChatMembers = groupChatName ? groupChatMemberBots(groupChatName, roster, allMeta) : []
|
||||
|
||||
if (groupChatName && groupChatMembers.length) {
|
||||
if (shouldRenderGroupChatInPane(groupChatName) && groupChatMembers.length) {
|
||||
return jsx(GroupChatWorkspace, { group: groupChatName, members: groupChatMembers })
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ function load(turnScript, { busyUntilResumeCall } = {}) {
|
||||
.replace(/^import .* from 'react\/jsx-runtime'\r?\n/m, '')
|
||||
.replace('export default {', 'globalThis.plugin = {')
|
||||
.concat(
|
||||
'\nglobalThis.__gc = { sendToGroupChat, runGroupChatRounds, harvestStrandedGroupReply, resolveGroupResponders, parseGroupChatMentions, rotateGroupSpeakers, isGroupPassText, formatGroupChatLine, buildGroupChatTurnPrompt, trimGroupChatLog, disbandGroupChat, updateGroupChat, openGroupChat, closeGroupChatMainTab, $groupChats, $groupNeedsYou, $groupChatWorkspace, $botMeta, GROUP_CHAT_MAX_ROUNDS, GROUP_CHAT_MAX_MESSAGES };\n'
|
||||
'\nglobalThis.__gc = { sendToGroupChat, runGroupChatRounds, harvestStrandedGroupReply, resolveGroupResponders, parseGroupChatMentions, rotateGroupSpeakers, isGroupPassText, formatGroupChatLine, buildGroupChatTurnPrompt, trimGroupChatLog, disbandGroupChat, updateGroupChat, openGroupChat, closeGroupChatMainTab, shouldRenderGroupChatInPane, $groupChats, $groupNeedsYou, $groupChatWorkspace, $botMeta, GROUP_CHAT_MAX_ROUNDS, GROUP_CHAT_MAX_MESSAGES };\n'
|
||||
)
|
||||
vm.runInNewContext(source, context, { filename: 'plugin.js' })
|
||||
const storageWrites = new Map()
|
||||
@@ -366,9 +366,30 @@ test('group selection follows main-window open and close', () => {
|
||||
|
||||
gc.openGroupChat('Core')
|
||||
assert.equal(gc.$groupChatWorkspace.get(), 'Core')
|
||||
// #89788: the main tab owns the room, so the pane keeps its roster.
|
||||
assert.equal(gc.shouldRenderGroupChatInPane('Core'), false)
|
||||
|
||||
onClose()
|
||||
assert.equal(gc.$groupChatWorkspace.get(), null)
|
||||
assert.equal(gc.shouldRenderGroupChatInPane('Core'), true)
|
||||
})
|
||||
|
||||
test('older and failed workspace hosts keep the in-pane group fallback', () => {
|
||||
const older = load(() => '(pass)')
|
||||
|
||||
older.openGroupChat('Core')
|
||||
assert.equal(older.$groupChatWorkspace.get(), 'Core')
|
||||
assert.equal(older.shouldRenderGroupChatInPane('Core'), true)
|
||||
|
||||
const failed = load(() => '(pass)')
|
||||
|
||||
failed.host.openWorkspace = () => {
|
||||
throw new Error('workspace unavailable')
|
||||
}
|
||||
|
||||
failed.openGroupChat('Ops')
|
||||
assert.equal(failed.$groupChatWorkspace.get(), 'Ops')
|
||||
assert.equal(failed.shouldRenderGroupChatInPane('Ops'), true)
|
||||
})
|
||||
|
||||
test('closing an older selected group does not clear the newer selection', () => {
|
||||
|
||||
Reference in New Issue
Block a user