From 72574baa4becfc1bfe1bbf5708c0167a339a800e Mon Sep 17 00:00:00 2001 From: operator-main Date: Sun, 4 Oct 2026 03:46:47 +0000 Subject: [PATCH] Replace Ctrl+J with click-based chat activation\n\n- New _ensure_chat_active(): compose -> switcher -> nav-chat priority\n- hatch-nav-chat click recovers from stripped /thread/new state\n- Ctrl+J needed keyboard focus which stripped states lack --- bin/muse-chat-api.py | 62 +++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/bin/muse-chat-api.py b/bin/muse-chat-api.py index 9f9b905..bf0d8d4 100755 --- a/bin/muse-chat-api.py +++ b/bin/muse-chat-api.py @@ -271,36 +271,38 @@ def cmd_sidechat_create(ws): """ import time as _time import json as _json - # Navigate to main chat first for known good state. - # (Previous create may have left browser on /thread/new with stripped DOM.) - # Uses Ctrl+J via CDP (proven method from cmd_sidechat_main). - # Fixed 2026-10-04 per reliability test: success poisons next run. - ws.send(_json.dumps({ - "id": 30, "method": "Input.dispatchKeyEvent", - "params": {"type": "keyDown", "key": "j", "code": "KeyJ", - "ctrlKey": True, "modifiers": 2} - })) - _json.loads(ws.recv()) - ws.send(_json.dumps({ - "id": 31, "method": "Input.dispatchKeyEvent", - "params": {"type": "keyUp", "key": "j", "code": "KeyJ", - "ctrlKey": True, "modifiers": 2} - })) - _json.loads(ws.recv()) - _time.sleep(3) - # Now open panel via switcher (should be on main chat with panel available) - # Check for + button; if not present, click switcher. - def _has_plus(): - return ev(ws, """(() => { - return !!document.querySelector('[data-testid="hatch-chat-compose"]'); - })()""") - - if not _has_plus(): - ev(ws, """(() => { - const sw = document.querySelector('[data-testid="hatch-chat-switcher-trigger"]'); - if (sw) sw.click(); - })()""") - _time.sleep(2) + import sys as _sys2 + # Ensure chat is active via DOM clicks (replaces Ctrl+J). + # Ctrl+J needs keyboard focus which stripped states (/thread/new) lack. + # Priority: compose (fast path) -> switcher -> nav-chat (recovery). + # Verified 2026-10-04: hatch-nav-chat click recovers from /thread/new. + def _ensure_chat_active(timeout=20): + t0 = _time.time() + while _time.time() - t0 < timeout: + st = ev(ws, """(() => ({ + compose: !!document.querySelector('[data-testid="hatch-chat-compose"]'), + switcher: !!document.querySelector('[data-testid="hatch-chat-switcher-trigger"]'), + navchat: !!document.querySelector('[data-testid="hatch-nav-chat"]') + }))()""") + if not isinstance(st, dict): + _time.sleep(1) + continue + if st.get("compose"): + return True + if st.get("switcher"): + ev(ws, """document.querySelector('[data-testid="hatch-chat-switcher-trigger"]').click()""") + _time.sleep(1.5) + continue + if st.get("navchat"): + ev(ws, """document.querySelector('[data-testid="hatch-nav-chat"]').click()""") + _time.sleep(2) + continue + _time.sleep(1) + return False + if not _ensure_chat_active(): + print("ERROR: Could not activate chat (no compose/switcher/nav-chat)", file=_sys2.stderr) + sys.exit(1) + print("Chat active", file=_sys2.stderr) # Find + button via data-testid (verified selector, SVG icon no text) result = ev(ws, """(() => { const btn = document.querySelector('[data-testid="hatch-chat-compose"]');