Skip to content

Commit f5dcf82

Browse files
authored
Merge pull request #315 from catsimple/fix_temporary_chat_selector
Fix temporary chat selector
2 parents f6e83b1 + 9af4f69 commit f5dcf82

2 files changed

Lines changed: 123 additions & 43 deletions

File tree

browser_utils/initialization/core.py

Lines changed: 116 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -560,33 +560,134 @@ async def signal_camoufox_shutdown() -> None: # pragma: no cover
560560
)
561561

562562

563-
async def enable_temporary_chat_mode(page: AsyncPage) -> None: # pragma: no cover
564-
"""
565-
Check and enable "Temporary chat" mode in the AI Studio interface.
566-
This is an independent UI operation and should be called after the page is fully stable.
563+
async def _is_temporary_chat_active(page: AsyncPage) -> bool:
564+
"""Best-effort check for whether Temporary chat is currently enabled.
565+
566+
Supports both the legacy toggle-class signal and the newer UI variants:
567+
- menu item checkmark (`data-test-incognito-checkmark`)
568+
- header indicator (`ms-incognito-mode-indicator`)
567569
"""
570+
568571
try:
569-
incognito_button_locator = page.locator(
570-
'button[aria-label="Temporary chat toggle"]'
572+
# Newer UI: header indicator appears only when Temporary chat is active.
573+
indicator_locator = page.locator(
574+
"ms-incognito-mode-indicator [data-test-id='main-text'], "
575+
"ms-incognito-mode-indicator .main-text"
571576
)
577+
indicator_count = await indicator_locator.count()
578+
for i in range(indicator_count):
579+
try:
580+
indicator_item = indicator_locator.nth(i)
581+
if not await indicator_item.is_visible(timeout=500):
582+
continue
583+
indicator_text = (await indicator_item.inner_text()).strip().lower()
584+
if indicator_text == "temporary chat":
585+
return True
586+
except Exception:
587+
continue
588+
589+
# Menu button variants (legacy and gray-release UI).
590+
toggle_locator = page.locator(
591+
"button[data-test-incognito-toggle], "
592+
'button[aria-label="Temporary chat toggle"], '
593+
'button[aria-label="Toggle temporary chat"]'
594+
)
595+
if await toggle_locator.count() > 0:
596+
# New UI: active state is represented by a checkmark inside the menu item.
597+
try:
598+
checkmark_locator = page.locator(
599+
"button[data-test-incognito-toggle] [data-test-incognito-checkmark], "
600+
'button[aria-label="Temporary chat toggle"] [data-test-incognito-checkmark], '
601+
'button[aria-label="Toggle temporary chat"] [data-test-incognito-checkmark]'
602+
)
603+
if (
604+
await checkmark_locator.count() > 0
605+
):
606+
return True
607+
except Exception:
608+
pass
609+
610+
# Legacy/alternative signals.
611+
for attr_name in ("aria-pressed", "aria-checked"):
612+
try:
613+
attr_value = await toggle_locator.get_attribute(attr_name)
614+
if attr_value and attr_value.lower() == "true":
615+
return True
616+
except Exception:
617+
pass
618+
619+
try:
620+
button_classes = await toggle_locator.get_attribute("class") or ""
621+
if "ms-button-active" in button_classes:
622+
return True
623+
except Exception:
624+
pass
625+
626+
return False
627+
except Exception:
628+
return False
629+
572630

573-
await incognito_button_locator.wait_for(state="visible", timeout=10000)
631+
async def enable_temporary_chat_mode(page: AsyncPage) -> bool: # pragma: no cover
632+
"""
633+
Check and enable "Temporary chat" mode in the AI Studio interface.
634+
Supports both direct UI visibility and collapsed menu visibility.
635+
"""
636+
incognito_selector = (
637+
"button[data-test-incognito-toggle], "
638+
'button[aria-label="Temporary chat toggle"], '
639+
'button[aria-label="Toggle temporary chat"]'
640+
)
641+
menu_trigger_selector = 'button[aria-label="View more actions"]'
574642

575-
button_classes = await incognito_button_locator.get_attribute("class")
643+
incognito_locator = page.locator(incognito_selector)
644+
menu_trigger = page.locator(menu_trigger_selector)
576645

577-
if button_classes and "ms-button-active" in button_classes:
646+
async def _close_menu_if_needed() -> None:
647+
"""Best-effort menu cleanup that tolerates UI variants without a menu trigger."""
648+
try:
649+
if await menu_trigger.count() == 0:
650+
return
651+
if await menu_trigger.is_visible():
652+
if await menu_trigger.get_attribute("aria-expanded") == "true":
653+
logger.debug("[UI] Closing menu to restore UI state")
654+
await page.keyboard.press("Escape")
655+
except Exception:
656+
pass
657+
658+
try:
659+
# Fast path
660+
logger.debug("[UI] Searching for temporary chat button (Fast path)")
661+
try:
662+
await incognito_locator.wait_for(state="visible", timeout=3000)
663+
except Exception:
664+
# Fallback
665+
logger.debug("[UI] Button not visible, attempting to open menu")
666+
if await menu_trigger.is_visible():
667+
await menu_trigger.click()
668+
# Wait for the menu item to appear
669+
await incognito_locator.wait_for(state="visible", timeout=5000)
670+
else:
671+
logger.warning("[UI] Neither button nor menu trigger found")
672+
return False
673+
674+
if await _is_temporary_chat_active(page):
578675
logger.debug("[UI] Temporary chat mode already active")
579676
else:
580-
await incognito_button_locator.click(timeout=5000, force=True)
677+
logger.debug("[UI] Enabling temporary chat mode")
678+
await incognito_locator.click(timeout=5000, force=True)
581679
await asyncio.sleep(1)
582680

583-
updated_classes = await incognito_button_locator.get_attribute("class")
584-
if updated_classes and "ms-button-active" in updated_classes:
585-
logger.debug("[UI] Temporary chat mode enabled")
586-
else:
587-
logger.warning("[UI] Failed to enable temporary chat mode")
681+
enabled = await _is_temporary_chat_active(page)
682+
683+
# Recovery: Close menu if still expanded
684+
await _close_menu_if_needed()
685+
return enabled
588686

589687
except asyncio.CancelledError:
590688
raise
591689
except Exception as e:
592690
logger.warning(f"[UI] Error in temporary chat mode: {e}")
691+
# Final safety attempt to clear any stuck UI
692+
await _close_menu_if_needed()
693+
return await _is_temporary_chat_active(page)

browser_utils/models/switcher.py

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -160,41 +160,20 @@ async def switch_ai_studio_model(page: AsyncPage, model_id: str, req_id: str) ->
160160
if page_display_match:
161161
try:
162162
logger.debug("[Model] Re-enabling temporary chat mode...")
163-
incognito_button_locator = page.locator(
164-
'button[aria-label="Temporary chat toggle"]'
165-
)
166-
167-
await incognito_button_locator.wait_for(
168-
state="visible", timeout=5000
169-
)
163+
from browser_utils.initialization import enable_temporary_chat_mode
170164

171-
button_classes = await incognito_button_locator.get_attribute(
172-
"class"
173-
)
174-
175-
if button_classes and "ms-button-active" in button_classes:
176-
logger.debug("[Model] Temporary chat mode already active")
165+
enabled = await enable_temporary_chat_mode(page)
166+
if enabled:
167+
logger.debug("[Model] Temporary chat mode enabled or already active")
177168
else:
178-
logger.debug("[Model] Clicking to open temporary chat mode...")
179-
await incognito_button_locator.click(timeout=3000)
180-
await asyncio.sleep(0.5)
181-
182-
updated_classes = await incognito_button_locator.get_attribute(
183-
"class"
169+
logger.warning(
170+
"[Model] Temporary chat mode state verification failed"
184171
)
185-
if updated_classes and "ms-button-active" in updated_classes:
186-
logger.debug("[Model] Temporary chat mode enabled")
187-
else:
188-
logger.warning(
189-
"Temporary chat mode state verification failed after click, may not have opened successfully."
190-
)
191172

192173
except asyncio.CancelledError:
193174
raise
194175
except Exception as e:
195-
logger.warning(
196-
f"Failed to re-enable temporary chat mode after model switching: {e}"
197-
)
176+
logger.warning(f"Failed to re-enable temporary chat mode after model switching: {e}")
198177

199178
# Invalidate function calling cache on model switch
200179
try:

0 commit comments

Comments
 (0)