Fix sidechat CDP and button selectors\n\n- sidechat_manager._ev: use id=1 and returnByValue to match ev()\n- cmd_sidechat_create: find + button via Side chats header proximity\n- cmd_sidechat_create: use cmd_send for initial message (not DOM hack)\n- Integrate ensure_sidebar with retry
This commit is contained in:
+34
-20
@@ -279,16 +279,40 @@ def cmd_sidechat_create(ws):
|
|||||||
if (btn) btn.click();
|
if (btn) btn.click();
|
||||||
})()""")
|
})()""")
|
||||||
_time.sleep(2)
|
_time.sleep(2)
|
||||||
|
# The + button sits next to the "Side chats" header (see screenshot 2026-10-04).
|
||||||
|
# It's not labeled "New side chat" - find it by proximity to the header text.
|
||||||
result = ev(ws, """(() => {
|
result = ev(ws, """(() => {
|
||||||
const btn = [...document.querySelectorAll('button')].find(b =>
|
// Strategy 1: Find button with "+" text near "Side chats"
|
||||||
|
const header = [...document.querySelectorAll('*')].find(el =>
|
||||||
|
el.textContent.trim() === 'Side chats' && el.children.length === 0);
|
||||||
|
if (header) {
|
||||||
|
// Look for + button in the header's parent or siblings
|
||||||
|
const parent = header.parentElement;
|
||||||
|
if (parent) {
|
||||||
|
const plus = [...parent.querySelectorAll('button')].find(b =>
|
||||||
|
(b.textContent||'').includes('+') || (b.getAttribute('aria-label')||'').toLowerCase().includes('add'));
|
||||||
|
if (plus) { plus.click(); return 'CLICKED_VIA_HEADER'; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Strategy 2: Find any button with "+" that's near the top
|
||||||
|
const plusBtn = [...document.querySelectorAll('button')].find(b => {
|
||||||
|
const t = (b.textContent||'').trim();
|
||||||
|
const al = (b.getAttribute('aria-label')||'').toLowerCase();
|
||||||
|
return t === '+' || al.includes('new side') || al.includes('add chat');
|
||||||
|
});
|
||||||
|
if (plusBtn) { plusBtn.click(); return 'CLICKED_VIA_PLUS'; }
|
||||||
|
// Strategy 3 (fallback): old aria-label exact match
|
||||||
|
const oldBtn = [...document.querySelectorAll('button')].find(b =>
|
||||||
(b.getAttribute('aria-label')||'') === 'New side chat');
|
(b.getAttribute('aria-label')||'') === 'New side chat');
|
||||||
if (!btn) return 'NOT_FOUND';
|
if (oldBtn) { oldBtn.click(); return 'CLICKED_VIA_ARIA'; }
|
||||||
btn.click();
|
return 'NOT_FOUND';
|
||||||
return 'CLICKED';
|
|
||||||
})()""")
|
})()""")
|
||||||
if result == 'NOT_FOUND':
|
if result == 'NOT_FOUND':
|
||||||
print("ERROR: 'New side chat' button not found (sidebar may be closed)", file=sys.stderr)
|
print("ERROR: New side chat + button not found (sidebar may be closed)", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
# Log which strategy worked for debugging
|
||||||
|
import sys as _sys
|
||||||
|
print(f"Sidechat create: {result}", file=_sys.stderr)
|
||||||
import time as _time
|
import time as _time
|
||||||
# Poll for URL to change to a thread URL (up to 15s)
|
# Poll for URL to change to a thread URL (up to 15s)
|
||||||
# Fixed 2026-10-04: was sleeping 5s and reading once, often captured
|
# Fixed 2026-10-04: was sleeping 5s and reading once, often captured
|
||||||
@@ -306,21 +330,11 @@ def cmd_sidechat_create(ws):
|
|||||||
# the first message. Send a system message to trigger ID assignment,
|
# the first message. Send a system message to trigger ID assignment,
|
||||||
# then re-read the URL.
|
# then re-read the URL.
|
||||||
if url.endswith("/thread/new"):
|
if url.endswith("/thread/new"):
|
||||||
ev(ws, """(() => {
|
# Use proven cmd_send to trigger real thread ID
|
||||||
const input = document.querySelector('[contenteditable="true"]');
|
try:
|
||||||
if (!input) return 'NO_INPUT';
|
cmd_send(ws, "[System: Side chat created]")
|
||||||
input.focus();
|
except Exception as e:
|
||||||
document.execCommand('insertText', false, '[System: Side chat created]');
|
print(f"Warning: initial send failed: {e}", file=sys.stderr)
|
||||||
return 'READY';
|
|
||||||
})()""")
|
|
||||||
_time.sleep(1)
|
|
||||||
# Click send
|
|
||||||
ev(ws, """(() => {
|
|
||||||
const btn = [...document.querySelectorAll('button')].find(b =>
|
|
||||||
(b.getAttribute('aria-label')||'').toLowerCase().includes('send'));
|
|
||||||
if (btn) { btn.click(); return 'SENT'; }
|
|
||||||
return 'NO_BTN';
|
|
||||||
})()""")
|
|
||||||
# Poll for URL to change from /thread/new to /thread/<uuid>
|
# Poll for URL to change from /thread/new to /thread/<uuid>
|
||||||
for i in range(15):
|
for i in range(15):
|
||||||
_time.sleep(1)
|
_time.sleep(1)
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ import json
|
|||||||
|
|
||||||
def _ev(ws, js, await_result=True):
|
def _ev(ws, js, await_result=True):
|
||||||
"""Evaluate JS via CDP, return result.
|
"""Evaluate JS via CDP, return result.
|
||||||
Uses id=1 to match muse-chat-api.py's ev() on shared websocket.
|
Matches muse-chat-api.py's ev() exactly (id=1, returnByValue).
|
||||||
Fixed 2026-10-04: was using id=100, causing response mismatch.
|
Fixed 2026-10-04: was using id=100, missing returnByValue.
|
||||||
"""
|
"""
|
||||||
import json as _json
|
import json as _json
|
||||||
ws.send(_json.dumps({
|
ws.send(_json.dumps({
|
||||||
"id": 1, "method": "Runtime.evaluate",
|
"id": 1, "method": "Runtime.evaluate",
|
||||||
"params": {"expression": js, "awaitPromise": True}
|
"params": {"expression": js, "returnByValue": True, "awaitPromise": await_result}
|
||||||
}))
|
}))
|
||||||
resp = _json.loads(ws.recv())
|
resp = _json.loads(ws.recv())
|
||||||
result = resp.get("result", {}).get("result", {})
|
result = resp.get("result", {}).get("result", {})
|
||||||
|
|||||||
Reference in New Issue
Block a user