diff --git a/SKILL.md b/SKILL.md index cd1b069..aceac09 100644 --- a/SKILL.md +++ b/SKILL.md @@ -49,10 +49,14 @@ Gemini 页面的操作按钮(`.send-button-container` 内)通过 `aria-label 4. 将用户提示词原样输入。 5. 发送后立即通知用户:正在绘图中。 6. **分段轮询等待**(见下方"CDP 保活轮询策略",生图超时上限 120s)。 -7. 结果出现后: - - 优先用"下载原图"按钮获取原图。 - - 若无下载按钮或失败,可对图片右键另存(通常是标清图)。 -8. 把图片返回用户;若有多张,按顺序全部回传。 +7. 结果出现后,调用 `GeminiOps.getLatestImage()` 获取最新生成的图片(Gemini 一次只生成一张): + - 返回 `{ok, src, alt, width, height, hasDownloadBtn}`。 + - 定位依据:`` — 只有同时具有 `image` 和 `loaded` 两个 class 的才是已渲染完成的生成图片;DOM 中取最后一个即为最新。 + - `src` 为 `https://lh3.googleusercontent.com/...` 格式的原图 URL。 + - 若 `ok === false`,等几秒再调一次;连续两次失败则做 snapshot 排查页面状态。 + - 若 `hasDownloadBtn: true`,可调用 `GeminiOps.downloadLatestImage()` 点击原图下载按钮。 + - 下载按钮定位:从 `img` 向上找到 `.image-container` 容器,容器内的 `mat-icon[fonticon="download"]` 即为下载原图按钮。 +8. 把图片返回用户。 ## CDP 保活轮询策略 diff --git a/references/gemini-flow.md b/references/gemini-flow.md index 208cb96..891902c 100644 --- a/references/gemini-flow.md +++ b/references/gemini-flow.md @@ -37,11 +37,63 @@ ## 4) 生图结果获取 -优先顺序: -1. 图片右上角"下载原图" -2. 右键另存为(标清) +Gemini 一次只生成一张图片,流程上只关心**最新生成的那张**,历史图片不做处理。 -下载到本地后再通过渠道回传。 +调用 `GeminiOps.getLatestImage()` 获取最新一张生成图片。 + +### DOM 结构 + +``` +
+ +
+ +
+
+``` + +### 图片定位 + +- 选择器:`img.image.loaded` +- `image` class = Gemini 的图片元素 +- `loaded` class = 图片已渲染完成(未加载完的不会有此 class) +- 两个 class 同时存在才算有效图片 +- DOM 中可能存在多张历史图片,**取最后一个**即为最新生成 + +### 下载按钮定位 + +- 从 `img` 向上找到最近的 `.image-container` 祖先容器 +- 在容器内查找 `mat-icon[fonticon="download"]`(即下载原图按钮) +- `getLatestImage()` 返回 `hasDownloadBtn` 字段标识是否有下载按钮 + +### API + +- `GeminiOps.getLatestImage()` → 获取最新一张图片信息 + +```json +{ + "ok": true, + "src": "https://lh3.googleusercontent.com/...", + "alt": "AI 生成", + "width": 1024, + "height": 1024, + "hasDownloadBtn": true +} +``` + +- `GeminiOps.downloadLatestImage()` → 点击最新图片的下载原图按钮 + +```json +{"ok": true, "src": "https://lh3.googleusercontent.com/..."} +``` + +### 回退 + +- `ok === false` → 页面可能还在渲染,等几秒再调一次 +- 连续两次失败 → 做 snapshot 排查页面状态 +- `hasDownloadBtn: false` → 回退到直接用 `src` URL 下载 ## 5) 用户提示文案(建议) diff --git a/scripts/gemini_ui_shortcuts.js b/scripts/gemini_ui_shortcuts.js index b7d6e25..1cf0b98 100644 --- a/scripts/gemini_ui_shortcuts.js +++ b/scripts/gemini_ui_shortcuts.js @@ -92,6 +92,67 @@ return {status:s.status, label:s.label, pageVisible:!document.hidden, ts:Date.now()}; } + /* ── 最新图片获取与下载 ── + * Gemini 一次只生成一张图片,流程上只关心最新生成的那张。 + * DOM 中 img.image.loaded 按顺序排列,最后一个即为最新生成。 + * + * DOM 结构: + *
+ * + *
+ * + *
+ *
+ */ + + function _findContainer(img){ + var el=img; + while(el&&el!==document.body){ + if(el.classList&&el.classList.contains('image-container')) return el; + el=el.parentElement; + } + return null; + } + + function _findDownloadBtn(container){ + if(!container) return null; + return container.querySelector('mat-icon[fonticon="download"]') + || container.querySelector('mat-icon[data-mat-icon-name="download"]') + || null; + } + + /** 获取最新生成的一张图片信息(DOM 中最后一个 img.image.loaded) */ + function getLatestImage(){ + var imgs=[...document.querySelectorAll('img.image.loaded')]; + if(!imgs.length) return {ok:false, error:'no_loaded_images'}; + var img=imgs[imgs.length-1]; + var container=_findContainer(img); + var dlBtn=_findDownloadBtn(container); + return { + ok: true, + src: img.src||'', + alt: img.alt||'', + width: img.naturalWidth||0, + height: img.naturalHeight||0, + hasDownloadBtn: !!dlBtn + }; + } + + /** 点击最新图片的"下载原图"按钮 */ + function downloadLatestImage(){ + var imgs=[...document.querySelectorAll('img.image.loaded')]; + if(!imgs.length) return {ok:false, error:'no_loaded_images'}; + var img=imgs[imgs.length-1]; + var container=_findContainer(img); + var dlBtn=_findDownloadBtn(container); + if(!dlBtn) return {ok:false, error:'download_btn_not_found'}; + var clickable=dlBtn.closest('button,[role="button"],.button-icon-wrapper')||dlBtn; + clickable.click(); + return {ok:true, src:img.src||''}; + } + function probe(){ var s=getStatus(); return { @@ -103,5 +164,5 @@ }; } - window.GeminiOps = {probe, click, fillPrompt, getStatus, pollStatus, selectors:S, version:'0.4.0'}; + window.GeminiOps = {probe, click, fillPrompt, getStatus, pollStatus, getLatestImage, downloadLatestImage, selectors:S, version:'0.7.0'}; })();