diff --git a/python_bridge/Auswahl_648.png b/python_bridge/Auswahl_648.png new file mode 100644 index 0000000..697e6e2 Binary files /dev/null and b/python_bridge/Auswahl_648.png differ diff --git a/python_bridge/chat_web_interface.py b/python_bridge/chat_web_interface.py index 91cc2c6..9e44718 100644 --- a/python_bridge/chat_web_interface.py +++ b/python_bridge/chat_web_interface.py @@ -679,12 +679,14 @@ class ClaudeChatInterface: if not claude_elements: try: # Claude.ai 2024/2025 nutzt oft conversation-turn Divs - # WICHTIG: Wir suchen die INNERSTEN Elemente die mit "Claude sagt:" BEGINNEN - # und NICHT in der Sidebar/Navigation sind + # WICHTIG: Wir suchen den CONTAINER der die gesamte Claude-Nachricht enthält, + # nicht nur das Element mit "Claude sagt:" - denn [READY] kann in einem + # separaten Child-Element stehen! found = self.driver.execute_script(""" const msgs = []; - // Suche nach Elementen die mit "Claude sagt:" BEGINNEN + // Strategie: Finde Elemente mit "Claude sagt:" und dann den + // ÜBERGEORDNETEN Container der die vollständige Nachricht enthält const allDivs = document.querySelectorAll('div, p, span'); for (const elem of allDivs) { // Überspringe wenn es ein user-message ist @@ -697,30 +699,63 @@ class ClaudeChatInterface: if (elem.closest('[class*="sidebar"]')) continue; if (elem.closest('[class*="Sidebar"]')) continue; - // Hole den direkten Text-Inhalt (nicht von Kindern) + // Hole den direkten Text-Inhalt const text = (elem.innerText || '').trim(); - // Text muss mit "Claude sagt:" BEGINNEN (nicht irgendwo enthalten) - // Das filtert Container-Elemente raus die die ganze Seite enthalten + // Text muss mit "Claude sagt:" BEGINNEN if (text.startsWith('Claude sagt:') && text.length > 20 && text.length < 10000) { - msgs.push(elem); + // Suche nach dem übergeordneten Container (z.B. prose-Container) + // der auch [READY] enthalten könnte + let container = elem; + let parent = elem.parentElement; + + // Gehe nach oben bis wir einen guten Container finden + // (max 5 Ebenen, um nicht die ganze Seite zu erfassen) + for (let i = 0; i < 5 && parent; i++) { + const parentText = (parent.innerText || '').trim(); + + // Stoppe wenn wir einen zu großen Container erreichen + if (parentText.length > 15000) break; + + // Stoppe wenn wir in user-message oder Navigation sind + if (parent.getAttribute('data-testid') === 'user-message') break; + if (parent.closest('nav')) break; + + // Prüfe ob der Parent-Container [READY] enthält und + // immer noch mit "Claude sagt:" beginnt + if (parentText.startsWith('Claude sagt:') || + parentText.includes('[READY]') || + parent.classList.contains('prose') || + parent.classList.contains('markdown')) { + container = parent; + } + + parent = parent.parentElement; + } + + msgs.push(container); } } - // Dedupliziere: Behalte nur die INNERSTEN Elemente - // (die den Text direkt enthalten, nicht Container) - const filtered = msgs.filter(div => { - // Behalte dieses Element nur wenn es KEIN anderes Element enthält + // Dedupliziere: Behalte nur die ÄUSSERSTEN (größten) Container + // (aber nicht zu groß - max 10000 Zeichen) + const unique = []; + for (const div of msgs) { + // Prüfe ob dieses Element schon in einem anderen enthalten ist + let isDuplicate = false; for (const other of msgs) { - if (other !== div && div.contains(other)) { - // Dieses Element enthält ein anderes -> verwerfen (zu groß) - return false; + if (other !== div && other.contains(div)) { + // Dieses Element ist in einem anderen enthalten + isDuplicate = true; + break; } } - return true; - }); + if (!isDuplicate && !unique.includes(div)) { + unique.push(div); + } + } - return filtered; + return unique; """) or [] if found: claude_elements = found @@ -1059,6 +1094,56 @@ class ClaudeChatInterface: logger.info(f"[READY] in bestehender Claude-Nachricht gefunden!") return True + # FALLBACK 2: Direkte DOM-Suche nach [READY] + # Claude.ai rendert [READY] manchmal in einem separaten Element + # das nicht als Teil der "Claude sagt:" Nachricht erkannt wird + try: + ready_found = self.driver.execute_script(""" + // Suche nach einem Element das EXAKT "[READY]" enthält + // WICHTIG: Muss NICHT in user-message sein und NICHT in den Instruktionen + const allElements = document.querySelectorAll('*'); + for (const elem of allElements) { + // Nur Blatt-Elemente (keine Container mit Kindern) + if (elem.children.length > 0) continue; + + const text = (elem.innerText || elem.textContent || '').trim(); + + // Muss [READY] enthalten + if (!text.includes('[READY]')) continue; + + // Prüfe dass es NICHT in user-message ist + if (elem.closest('[data-testid="user-message"]')) continue; + + // Prüfe dass es NICHT in der Sidebar ist + if (elem.closest('nav')) continue; + if (elem.closest('[class*="sidebar"]')) continue; + + // Wichtig: Text darf NICHT mit "## WICHTIG" beginnen + // (das wäre ein Heading in den Instruktionen) + if (text.startsWith('## WICHTIG') || text.startsWith('**WICHTIG')) continue; + + // Text darf nicht "Bestätige mit [READY]" enthalten + // (das wäre eine Anweisung, keine Bestätigung) + if (text.includes('Bestätige mit')) continue; + if (text.includes('antworte mit')) continue; + if (text.includes('Format muss EXAKT')) continue; + + // Wenn der Text kurz ist (<100 Zeichen), ist es wahrscheinlich + // Claudes echte [READY] Bestätigung + // Wenn er lang ist, ist es wahrscheinlich ein Container der Instruktionen enthält + if (text.length > 200) continue; + + console.log('Found [READY] in:', text.substring(0, 50)); + return true; + } + return false; + """) + if ready_found: + logger.info(f"[READY] via direkte DOM-Suche gefunden!") + return True + except Exception as e: + logger.debug(f"DOM-Suche nach [READY] fehlgeschlagen: {e}") + # Kurz warten bevor nächster Check time.sleep(1)