From 104e55d613e2e8fe5bc2af0a39a582f962ce87fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A7=E9=A9=8A?= Date: Fri, 3 Apr 2026 01:26:43 +0800 Subject: [PATCH] fix: Add proper page ready check before finding input Key improvements: - Wait for page to fully load (SPA needs time) - Wait for input field to be visible before typing - Check login status only after page is ready - Add fallback wait (3 seconds) if first attempt fails - Properly handle error messages Based on project-golem's PageInteractor.waitForReady() --- .../geminiweb/playwright_provider.go | 85 +++++++++++++------ 1 file changed, 61 insertions(+), 24 deletions(-) diff --git a/internal/providers/geminiweb/playwright_provider.go b/internal/providers/geminiweb/playwright_provider.go index 577dcaa..dd2a0dc 100644 --- a/internal/providers/geminiweb/playwright_provider.go +++ b/internal/providers/geminiweb/playwright_provider.go @@ -132,10 +132,15 @@ func (p *PlaywrightProvider) Generate(ctx context.Context, model string, message }); err != nil { return fmt.Errorf("failed to navigate: %w", err) } - time.Sleep(2 * time.Second) } - // 3. 檢查登入狀態 + // 3. 等待頁面完全載入(project-golem 策略) + fmt.Println("[GeminiWeb] Waiting for page to be ready...") + if err := p.waitForPageReady(); err != nil { + fmt.Printf("[GeminiWeb] Warning: %v\n", err) + } + + // 4. 檢查登入狀態 fmt.Println("[GeminiWeb] Checking login status...") loggedIn := p.isLoggedIn() if !loggedIn { @@ -151,11 +156,6 @@ func (p *PlaywrightProvider) Generate(ctx context.Context, model string, message fmt.Println("[GeminiWeb] Logged in") } - // 4. 等待頁面就緒 - if err := p.waitForReady(); err != nil { - fmt.Printf("[GeminiWeb] Warning: %v\n", err) - } - // 5. 建構提示詞 prompt := buildPromptFromMessagesPlaywright(messages) fmt.Printf("[GeminiWeb] Typing prompt (%d chars)...\n", len(prompt)) @@ -201,6 +201,58 @@ func (p *PlaywrightProvider) Close() error { return nil } +// waitForPageReady 等待頁面完全就緒(project-golem 策略) +func (p *PlaywrightProvider) waitForPageReady() error { + fmt.Println("[GeminiWeb] Checking for ready state...") + + // 1. 等待停止按鈕消失(如果存在) + _, _ = p.page.WaitForSelector("button[aria-label*='Stop'], button[aria-label*='停止']", playwright.PageWaitForSelectorOptions{ + State: playwright.WaitForSelectorStateDetached, + Timeout: playwright.Float(5000), + }) + + // 2. 等待輸入框出現(關鍵!) + inputSelectors := []string{ + ".ProseMirror", + "rich-textarea", + "div[role='textbox']", + "div[contenteditable='true']", + "textarea", + } + + var lastErr error + for _, sel := range inputSelectors { + fmt.Printf(" Checking for: %s\n", sel) + locator := p.page.Locator(sel) + if err := locator.WaitFor(playwright.LocatorWaitForOptions{ + Timeout: playwright.Float(10000), + State: playwright.WaitForSelectorStateVisible, + }); err == nil { + fmt.Printf(" ✓ Input field found: %s\n", sel) + return nil + } else { + lastErr = err + } + } + + // 3. 如果都找不到,給頁面更多時間 + fmt.Println("[GeminiWeb] Input not found immediately, waiting longer...") + time.Sleep(3 * time.Second) + + for _, sel := range inputSelectors { + locator := p.page.Locator(sel) + if err := locator.WaitFor(playwright.LocatorWaitForOptions{ + Timeout: playwright.Float(5000), + State: playwright.WaitForSelectorStateVisible, + }); err == nil { + fmt.Printf(" ✓ Input field found after wait: %s\n", sel) + return nil + } + } + + return fmt.Errorf("input field not ready: %w", lastErr) +} + // isLoggedIn 檢查是否已登入 func (p *PlaywrightProvider) isLoggedIn() bool { // 嘗試找輸入框(登入狀態的主要特徵) @@ -213,29 +265,14 @@ func (p *PlaywrightProvider) isLoggedIn() bool { } for _, sel := range selectors { - if _, err := p.page.WaitForSelector(sel, playwright.PageWaitForSelectorOptions{ - Timeout: playwright.Float(3000), - }); err == nil { + locator := p.page.Locator(sel) + if count, _ := locator.Count(); count > 0 { return true } } return false } -// waitForReady 等待頁面就緒 -func (p *PlaywrightProvider) waitForReady() error { - fmt.Println("[GeminiWeb] Checking if page is ready...") - - // 等待停止按鈕消失(如果存在) - _, _ = p.page.WaitForSelector("button[aria-label*='Stop']", playwright.PageWaitForSelectorOptions{ - State: playwright.WaitForSelectorStateDetached, - Timeout: playwright.Float(5000), - }) - - fmt.Println("[GeminiWeb] Page is ready") - return nil -} - // typeInput 輸入文字(使用 Playwright 的 Auto-wait) func (p *PlaywrightProvider) typeInput(text string) error { fmt.Println("[GeminiWeb] Looking for input field...")