chrome-ai-bridge 1.0.9 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -188,64 +188,71 @@ export const askGeminiImage = defineTool({
188
188
  response.appendResponseLine('⚠️ 送信ボタンが見つかりません (Enterキーを試行)');
189
189
  }
190
190
  response.appendResponseLine('🎨 画像生成中... (1-2分かかることがあります)');
191
- // Wait for image generation using MutationObserver for instant detection
191
+ // Wait for image generation using MutationObserver + polling hybrid approach
192
+ // MutationObserver provides instant detection, polling ensures we don't miss state
192
193
  const startTime = Date.now();
193
194
  const maxWaitTime = 180000; // 3 minutes
194
- const imageFound = await page.evaluate((maxWait) => {
195
- return new Promise(resolve => {
196
- // Check if image already exists (immediate return)
197
- const checkCompletion = () => {
198
- // Check for generated image
199
- const images = document.querySelectorAll('img[src*="blob:"], img[src*="generated"]');
200
- // Check for download button with various detection methods
201
- const buttons = Array.from(document.querySelectorAll('button, [role="menuitem"]'));
202
- const hasDownload = buttons.some(b => {
203
- const text = b.textContent || '';
204
- const ariaLabel = b.getAttribute('aria-label') || '';
205
- const describedBy = b.getAttribute('aria-describedby');
206
- let desc = '';
207
- if (describedBy) {
208
- const descEl = document.getElementById(describedBy);
209
- desc = descEl?.textContent || '';
210
- }
211
- return (text.includes('ダウンロード') ||
212
- text.includes('Download') ||
213
- text.includes('フルサイズ') ||
214
- ariaLabel.toLowerCase().includes('download') ||
215
- desc.includes('フルサイズ') ||
216
- desc.includes('ダウンロード'));
217
- });
218
- if (images.length > 0 || hasDownload) {
219
- return true;
220
- }
221
- return false;
222
- };
223
- // Initial check
224
- if (checkCompletion()) {
225
- resolve(true);
226
- return;
227
- }
228
- // Set up MutationObserver for instant detection
229
- const observer = new MutationObserver(() => {
230
- if (checkCompletion()) {
231
- observer.disconnect();
232
- clearTimeout(timeoutId);
233
- resolve(true);
195
+ // Set up MutationObserver in the page (stores result in window object)
196
+ await page.evaluate(() => {
197
+ // @ts-expect-error - window property
198
+ window.__geminiImageFound = false;
199
+ const checkCompletion = () => {
200
+ const images = document.querySelectorAll('img[src*="blob:"], img[src*="generated"]');
201
+ const buttons = Array.from(document.querySelectorAll('button, [role="menuitem"]'));
202
+ const hasDownload = buttons.some(b => {
203
+ const text = b.textContent || '';
204
+ const ariaLabel = b.getAttribute('aria-label') || '';
205
+ const describedBy = b.getAttribute('aria-describedby');
206
+ let desc = '';
207
+ if (describedBy) {
208
+ const descEl = document.getElementById(describedBy);
209
+ desc = descEl?.textContent || '';
234
210
  }
211
+ return (text.includes('ダウンロード') ||
212
+ text.includes('Download') ||
213
+ text.includes('フルサイズ') ||
214
+ ariaLabel.toLowerCase().includes('download') ||
215
+ desc.includes('フルサイズ') ||
216
+ desc.includes('ダウンロード'));
235
217
  });
236
- observer.observe(document.body, {
237
- childList: true,
238
- subtree: true,
239
- attributes: true,
240
- attributeFilter: ['src', 'aria-label', 'aria-describedby'],
241
- });
242
- // Timeout fallback
243
- const timeoutId = setTimeout(() => {
218
+ return images.length > 0 || hasDownload;
219
+ };
220
+ // Initial check
221
+ if (checkCompletion()) {
222
+ // @ts-expect-error - window property
223
+ window.__geminiImageFound = true;
224
+ return;
225
+ }
226
+ // Set up MutationObserver
227
+ const observer = new MutationObserver(() => {
228
+ if (checkCompletion()) {
229
+ // @ts-expect-error - window property
230
+ window.__geminiImageFound = true;
244
231
  observer.disconnect();
245
- resolve(false);
246
- }, maxWait);
232
+ }
247
233
  });
248
- }, maxWaitTime);
234
+ observer.observe(document.body, {
235
+ childList: true,
236
+ subtree: true,
237
+ attributes: true,
238
+ attributeFilter: ['src', 'aria-label', 'aria-describedby'],
239
+ });
240
+ });
241
+ // Poll for the result (short intervals to minimize latency)
242
+ let imageFound = false;
243
+ while (Date.now() - startTime < maxWaitTime) {
244
+ // Check if MutationObserver detected image
245
+ const found = await page.evaluate(() => {
246
+ // @ts-expect-error - window property
247
+ return window.__geminiImageFound === true;
248
+ });
249
+ if (found) {
250
+ imageFound = true;
251
+ break;
252
+ }
253
+ // Short wait before next check (500ms for responsiveness)
254
+ await new Promise(resolve => setTimeout(resolve, 500));
255
+ }
249
256
  if (!imageFound) {
250
257
  response.appendResponseLine('❌ 画像生成タイムアウト (3分)');
251
258
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrome-ai-bridge",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "MCP server bridging Chrome browser and AI assistants (ChatGPT, Gemini). Browser automation + AI consultation.",
5
5
  "type": "module",
6
6
  "bin": "./scripts/cli.mjs",