Gemini Web Interface
Automates gemini.google.com via Chrome DevTools MCP. Every locator tested against live DOM. Every state transition verified.
Prerequisites
- Chrome DevTools MCP connected
- Logged into gemini.google.com
Defaults
- Model: 3.1 Pro + Extended (the highest tier). Always use unless user explicitly specifies otherwise.
- Image output: Download to
<project>/gemini_output/<title>/with two subfolders:original/— button-downloaded full-resolution file (MUST usebutton "Download full size image")cleaned/— after reverse alpha blending watermark removal
URL Patterns
| Action | URL |
|---|---|
| New chat | https://gemini.google.com/app |
| Specific chat | https://gemini.google.com/app/<chat_id> (NOT c_<id>) |
| Search chats | https://gemini.google.com/search |
| All notebooks | https://gemini.google.com/notebooks/view |
| Specific notebook | https://gemini.google.com/notebook/<uuid> |
| Create notebook | https://gemini.google.com/notebooks/create |
Dynamic UIDs
Every UID changes on each page load. After ANY click or navigation, snapshot. Never cache UIDs.
Core Elements
| Element | Locator |
|---|---|
| New chat | link "New chat" in navigation "Main actions menu" (Ctrl+Shift+O) |
| Textbox | textbox "Enter a prompt for Gemini" (Quill.js, multiline) |
| Send button | button "Send message" → becomes "Stop response" during generation |
| Upload & tools | button "Upload & tools" (the + button) |
| Mode picker | button label starts "Open mode picker" |
| Temporary chat | button "Temporary chat" (vanishes after 1st message in conversation) |
| Conversation menu | button "Open menu for conversation actions." (after 1st message) |
| Edit textbox | textbox "Edit prompt" (NOT the main textbox) |
Complete "+" Menu Tree
button "Upload & tools"
└── menu "Menu options"
├── menu "Upload file options"
│ ├── menuitem "Upload files. Documents, data, code files" → opens file picker
│ └── menuitem "Add from Drive. Sheets, Docs, Slides" → Drive picker dialog
├── button "More uploads" → expands submenu:
│ └── group "More upload options"
│ ├── menuitem "Import code" → dialog: GitHub URL + Upload folder
│ └── menuitem "Notebooks" → import from notebook
├── menuitemcheckbox "Create image" → image gen mode (20 templates, Nano Banana 2)
├── menuitemcheckbox "Create video" → video gen mode (18 templates, Omni, 16:9)
└── button "More tools" → expands submenu:
├── menuitemcheckbox "Deep research"
├── menuitemcheckbox "Create music"
├── menuitemcheckbox "Guided learning"
└── switch "Personal Intelligence" → disable for clean responses
Mode toggles: Create image adds button "Deselect Images", Create video adds button "Deselect Videos". Click to exit the mode.
"Import code" dialog:
dialog
heading "Import code"
textbox "GitHub repository or branch URL"
button "Upload folder"
button "Import"
button "Cancel code import"
Complete Settings Menu
button "Settings" (bottom of sidebar, haspopup="menu")
└── menu (orientation="vertical")
├── menuitem "Activity"
├── menuitem "Personal Intelligence"
├── menuitem "Import memory to Gemini"
├── menuitem "Usage Limits"
├── menuitem "Scheduled actions"
├── menuitem "Gems"
├── menuitem "Your public links"
├── menuitem "Theme" → submenu: System / Light / Dark (menuitemradio)
├── menuitem "Manage subscription"
├── menuitem "Upgrade to Google AI Ultra"
├── menuitem "NotebookLM"
├── menuitem "Send feedback"
├── menuitem "Help" → has submenu
├── menuitem "Portland, OR, USA ..." → location indicator
└── menuitem "Update location"
Models & Thinking
Same 3×2 picker applies to ALL modes (chat, image gen, video gen). Substring match — "Selected " prefix varies. Selecting a model closes the menu; reopen for thinking level.
| Model | Match |
|---|---|
| 3.1 Flash-Lite | "Flash-Lite" |
| 3.5 Flash | "3.5 Flash" |
| 3.1 Pro | "3.1 Pro" |
Thinking: menuitem starts with "Thinking level" → "Standard" / "Extended". Default: 3.1 Pro + Extended.
Image gen label shows "Nano Banana 2", video gen shows "Omni" — these are descriptive, not separate pickers.
Response State Machine
| State | Button | Edit btn | Response area |
|---|---|---|---|
| Idle | "Send message" enabled |
N/A | N/A |
| Generating | "Stop response" |
disabled | generic busy |
| Complete | "Send message" disabled |
enabled | live="polite", button "Good response" + "Bad response" |
wait_for completion: ["Good response", "Bad response"] — thumbs buttons, only appear when done.
Response Page Structure
Text response:
heading "You said <prompt>"
button "Copy prompt" button "Edit"
heading "Gemini said"
generic live="polite" → StaticText <response>
button "Good response" button "Bad response"
button "Redo" → submenu: Longer / Shorter / Don't personalize / Try again
button "Copy" button "Show more options"
Image response (inside generic live="polite"):
button ", AI generated" ← watermark label
button "Share image" ← share dialog
button "Copy image" ← clipboard
button "Download full size image" ← browser download
Image is blob: URL. Extract via canvas: drawImage → toDataURL('image/png').
"Show more options" menu:
menuitem "Copy image"
menuitem "Download image"
menuitem "Listen"
menuitem "Redo with Pro"
menuitem "Report legal issue"
menuitem "See thinking steps"
Flows
A: New Chat
navigate → /app → snapshot (verify textbox+sidebar)
click "Upload & tools" → snapshot → click "More tools" (NOT "More uploads")
snapshot → if switch "Personal Intelligence" checked → click to disable
Escape
B: Resume Chat
Sidebar: navigate /app → if Recents collapsed click "Toggle Recents" → click chat link
URL: navigate /app/<chat_id>
C: Model + Thinking
click "Open mode picker, ..." → snapshot → click model menuitem
(reopen picker — model selection closes menu)
click "Open mode picker, ..." → click "Thinking level ..." → click "Standard"/"Extended"
Escape
D: File Upload (Bridge)
1. evaluate_script: create <input id="mcp-bridge" type="file" multiple> (idempotent)
2. snapshot → find "MCP bridge" → upload_file to bridge uid
3. click "Upload & tools" → snapshot → click "Upload files. Documents, data, code files"
(creates input[name="Filedata"] in DOM, file picker opens — ignore it)
4. evaluate_script: DataTransfer bridge.files → input[name="Filedata"] → dispatch change event
Files appear as chips. Send enables ~1-2s. Multi-file: stash pattern (see below).
E: Send & Read
fill textbox (must use fill — Quill.js CSP blocks evaluate_script)
click "Send message" → wait_for ["Good response", "Bad response"]
snapshot → extract text from generic live="polite" after "Gemini said"
F: Stop Mid-Generation
wait_for ["Stop response"] → click "Stop response"
wait_for ["Good response", "Bad response", "Send message"]
G: Edit & Resend
click "Edit" on user message → fill textbox "Edit prompt" → click "Update"
Update disabled until text differs from original.
wait_for ["Good response", "Bad response"]
H: Redo
click "Redo" → submenu: "Longer" / "Shorter" / "Don't personalize" / "Try again"
click option → wait_for ["Good response", "Bad response"]
I: Image Generation + Download + Watermark Removal
Prerequisite: model MUST be 3.1 Pro + Extended (Flow C) unless user overrides.
Generate:
click "Upload & tools" → click menuitemcheckbox "Create image"
fill textbox → send
wait_for ["Good response", "Bad response"]
Download ORIGINAL (full-resolution, MUST use button):
click button "Download full size image"
→ file lands in ~/Downloads/ as Gemini_Generated_Image_<hash>.png
→ 2816x1536 typical, ~5.5MB (canvas re-encode is only 1024x559, 139KB — DO NOT use canvas)
Organize files:
mkdir <project>/gemini_output/<chat_title_or_user_query>/
mkdir <project>/gemini_output/<title>/original/
mkdir <project>/gemini_output/<title>/cleaned/
Move downloaded file → original/
Remove watermark: See references/reverse-alpha-blending.md for theory.
Run: uv run --with Pillow --with numpy scripts/remove_watermark.py <input> --alpha 0.01 -o cleaned/cleaned.png
Exit image mode: click button "Deselect Images".
J: Video Generation
click "Upload & tools" → click menuitemcheckbox "Create video"
Mode activates: 18 templates, "Create with Omni" label, 16:9 aspect
fill textbox → send (uses same 3×2 model picker)
Exit: click "Deselect Videos"
K: Import Code
click "Upload & tools" → click "More uploads" → click "Import code"
dialog with: textbox "GitHub repository or branch URL" + button "Upload folder" + button "Import"
L: Notebooks
Create:
navigate /notebooks/create → snapshot → fill textbox "Name of the notebook"
snapshot → click unlabeled button (appears after text) → wait_for ["Add sources"]
Add sources:
click "Add sources" → dialog "Sources" with 4 options:
- menuitem "Upload files. Documents, data, code files" (bridge, same as Flow D)
- menuitem "Add from Drive. Sheets, Docs, Slides"
- menuitem "Add websites" → sub-dialog "Website URLs" with textbox "Paste any links"
- menuitem "Copied text" → sub-dialog with textbox "Pasted text"
Chat: Sending prompt in a notebook redirects to /app/. Chat appears as button "Navigate to a recent chat in a notebook" in the notebook view. Without sources, it's a regular chat.
Delete:
click "Notebook settings" → click "Delete" → dialog "Delete this notebook?"
click "Delete everywhere" → redirects to /notebooks/view
Notebook settings menu: Notebook settings / Pin|Unpin / Rename / Delete
Multi-File Upload (Stash)
upload_file replaces bridge.files each call. For N files:
create bridge → window._stash = []
upload file1 → window._stash = Array.from(bridge.files)
upload file2 →
dt = new DataTransfer()
for (f of [...stash, ...bridge.files]) dt.items.add(f)
bridge.files = dt.files
proceed with Flow D steps 3-4. Cleanup: delete window._stash
Bridge Cleanup
evaluate_script: document.getElementById('mcp-bridge')?.remove()
Error Recovery
| Symptom | Fix |
|---|---|
| "Gemini input not found" | Re-click "Upload & tools" → "Upload files" |
| Send disabled after upload | Wait 2-3s, files still processing |
| Bridge empty after navigation | SPA preserves, full nav kills. Recreate. |
| Mode picker closed | Reopen (model selection auto-closes menu) |
| Menuitem not found | Substring match, not exact |
| "Update" disabled in edit | Text must differ from original |
| Wrong chat via URL | Use /app/<id> not /app/c_<id> |
| Menu click off-target | Close menus first with Escape, re-snapshot |
Limitations
- Quill.js CSP: prompt MUST use
fill, not evaluate_script - Bridge dies on full-page nav; SPA nav preserves it
- upload_file replaces bridge.files; use stash for multi
- UIDs volatile: snapshot after every click
- Model menu closes on select: reopen for thinking level
- Edit uses dedicated
textbox "Edit prompt"separate from main textbox - Model list hardcoded; verify with snapshot if selection fails
- Temporary chat button disappears after first message in conversation