fix: 再次修复 Gemini 懒加载的问题

This commit is contained in:
foxhui
2025-12-28 02:09:03 +08:00
Unverified
parent c88c34aa4a
commit e8733f0953
3 changed files with 63 additions and 6 deletions
+3 -3
View File
@@ -5,6 +5,7 @@
import {
sleep,
safeClick,
safeScroll,
uploadFilesViaChooser
} from '../engine/utils.js';
import {
@@ -173,9 +174,8 @@ async function generate(context, prompt, imgPaths, modelId, meta = {}) {
meta
});
// 等待图片元素出现并滚动到可视范围,触发懒加载
await scrollToElement(page, 'model-response', { timeout: 20000 });
// 将图片滚动到可视范围,触发懒加载
await scrollToElement(page, 'generated-image', { timeout: 120000 });
imageResponse = await imageResponsePromise;
} catch (e) {
const pageError = normalizePageError(e, meta);
+3 -2
View File
@@ -8,6 +8,7 @@ import {
uploadFilesViaChooser
} from '../engine/utils.js';
import {
fillPrompt,
normalizePageError,
moveMouseAway,
waitForInput,
@@ -123,7 +124,7 @@ async function generate(context, prompt, imgPaths, modelId, meta = {}) {
if (await modelCombobox.count() > 0) {
await safeClick(page, modelCombobox.first(), { bias: 'button' });
await sleep(300, 500);
await safeClick(page, page.getByRole('option', { name: codeName }), { bias: 'button' });
await safeClick(page, page.getByRole('option', { name: codeName, exact: true }), { bias: 'button' });
await sleep(300, 500);
logger.debug('适配器', `模型已设置为 ${codeName}`, meta);
}
@@ -185,7 +186,7 @@ async function generate(context, prompt, imgPaths, modelId, meta = {}) {
logger.info('适配器', '输入提示词...', meta);
const textarea = page.locator('textarea[placeholder]');
await waitForInput(page, textarea, { click: true });
await textarea.fill(prompt);
await fillPrompt(page, textarea, prompt, meta);
await sleep(500, 1000);
// 7. 先启动 API 监听,再点击发送
+57 -1
View File
@@ -5,7 +5,7 @@
* 职责边界:
* - 浏览器原子操作(点击、输入、上传等)
* - 页面状态检测(isPageValid、createPageCloseWatcher
* - 拟人化交互(humanType、safeClick
* - 拟人化交互(humanType、safeClick、safeScroll
* - 工具函数(random、sleep、getMimeType
*
* 注意:业务逻辑应放在 backend/utils.js
@@ -200,6 +200,62 @@ export async function safeClick(page, target, options = {}) {
}
}
/**
* 安全滚动 (包含拟人化移动和滚轮滚动)
* 支持 CSS selector、ElementHandle 和 Locator 三种输入
* @param {import('playwright-core').Page} page - Playwright 页面对象
* @param {string|import('playwright-core').ElementHandle|import('playwright-core').Locator} target - CSS 选择器、元素句柄或 Locator
* @param {object} [options] - 滚动选项
* @param {number} [options.deltaX=0] - 水平滚动距离 (正值向右)
* @param {number} [options.deltaY=0] - 垂直滚动距离 (正值向下)
* @param {string} [options.bias='random'] - 偏移偏好: 'input' 或 'random'
* @returns {Promise<void>}
*/
export async function safeScroll(page, target, options = {}) {
try {
let el;
// 判断输入类型
if (typeof target === 'string') {
// CSS selector
el = await page.$(target);
if (!el) throw new Error(`未找到: ${target}`);
} else if (typeof target.elementHandle === 'function') {
// Locator (来自 page.getByRole, page.getByText 等)
el = await target.elementHandle();
if (!el) throw new Error(`Locator 未匹配到元素`);
} else {
// ElementHandle
el = target;
if (!el || !el.asElement()) throw new Error(`Element handle invalid`);
}
const deltaX = options.deltaX || 0;
const deltaY = options.deltaY || 0;
// 使用 ghost-cursor hover 后滚动
if (page.cursor) {
const box = await el.boundingBox();
if (box) {
const { x, y } = getHumanClickPoint(box, options.bias || 'random');
await page.cursor.moveTo({ x, y });
await page.mouse.wheel(deltaX, deltaY);
return;
}
// 如果无法获取 box,降级到元素中心点滚动
await page.cursor.move(el);
await page.mouse.wheel(deltaX, deltaY);
return;
}
// 降级逻辑: 直接在元素上 hover 并滚动
await el.hover();
await page.mouse.wheel(deltaX, deltaY);
} catch (err) {
throw err;
}
}
/**
* 模拟人类键盘输入
* 支持 CSS selector 和 ElementHandle 两种输入