From dcd10de735eb800333f0a4e4ee32edcff2599f2d Mon Sep 17 00:00:00 2001 From: Alex MacDonell Date: Thu, 23 Apr 2026 11:39:11 -0700 Subject: [PATCH] fix(browser_utils): type via page.keyboard to survive NotebookLM re-renders StealthUtils.human_type captures an ElementHandle once and types into it char-by-char. Against the current NotebookLM UI, the `textarea.query-box-input` is re-mounted by React on the first keystroke, which detaches the handle and raises: patchright._impl._errors.Error: ElementHandle.type: Element is not attached to the DOM Switching to `page.keyboard.type` (which targets the currently-focused element at the moment of each keystroke) survives the re-render without losing the human-like per-character delay and occasional pause. Repro: install the skill on a fresh env, auth, run ask_question.py against any notebook. The typing loop fails after 1 character. Tested locally (Windows 11, Chrome 131, patchright 1.55.2): fix lands the question intact and completes the full query-and-answer flow. The rest of the stealth loop (click-to-focus, per-char delay, random 5% micro-pause) is unchanged. --- scripts/browser_utils.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/scripts/browser_utils.py b/scripts/browser_utils.py index 60a1210..714166d 100755 --- a/scripts/browser_utils.py +++ b/scripts/browser_utils.py @@ -81,10 +81,15 @@ def human_type(page: Page, selector: str, text: str, wpm_min: int = 320, wpm_max # Click to focus element.click() - - # Type + + # Type via page-level keyboard. Typing on the stored ElementHandle + # fails against NotebookLM's current UI because the textarea is + # re-rendered on first keystroke (React re-mount), which detaches + # the handle and raises `ElementHandle.type: Element is not attached + # to the DOM`. page.keyboard.type targets whatever element has + # focus at the moment of the keystroke, so it survives re-renders. for char in text: - element.type(char, delay=random.uniform(25, 75)) + page.keyboard.type(char, delay=random.uniform(25, 75)) if random.random() < 0.05: time.sleep(random.uniform(0.15, 0.4))