|
1 | 1 | /* ────────────────────────────────────────────── |
2 | | - * Content-script – Gemini |
| 2 | + * Content-script – Gemini (ISOLATED world) |
3 | 3 | * |
4 | | - * Two responsibilities: |
5 | | - * 1. Monkey-patch window.fetch in MAIN world to |
6 | | - * intercept Google's batch RPC responses. |
| 4 | + * Responsibilities: |
| 5 | + * 1. Listen for postMessage from the MAIN-world |
| 6 | + * fetch interceptor (gemini-main.ts) and |
| 7 | + * forward intercepted chats to the background. |
7 | 8 | * 2. Inject Save + Restore overlay buttons. |
8 | | - * |
9 | | - * NOTE: CRXJS will inject this as an ISOLATED |
10 | | - * content script. To run in MAIN world, we |
11 | | - * inject a <script> element. Communication |
12 | | - * goes through window.postMessage. |
13 | 9 | * ────────────────────────────────────────────── */ |
14 | 10 |
|
15 | 11 | import React from "react"; |
16 | 12 | import { mountWidget } from "./mount"; |
17 | 13 | import { FloatingDial } from "./FloatingDial"; |
18 | 14 |
|
19 | | -/* ── 1. MAIN-world fetch interceptor ──────────────────────── */ |
20 | | - |
21 | | -const interceptorCode = ` |
22 | | -(function() { |
23 | | - const _fetch = window.fetch; |
24 | | - window.fetch = async function(...args) { |
25 | | - const response = await _fetch.apply(this, args); |
26 | | -
|
27 | | - try { |
28 | | - const url = typeof args[0] === 'string' ? args[0] : args[0]?.url ?? ''; |
29 | | - // Google Gemini uses /batchexecute for its RPC calls |
30 | | - if (url.includes('batchexecute')) { |
31 | | - const clone = response.clone(); |
32 | | - clone.text().then(text => { |
33 | | - window.postMessage({ |
34 | | - type: '__HACKLM_MIGRATE_GEMINI_INTERCEPT__', |
35 | | - url, |
36 | | - body: text, |
37 | | - }, '*'); |
38 | | - }).catch(() => {}); |
39 | | - } |
40 | | - } catch (_) {} |
41 | | -
|
42 | | - return response; |
43 | | - }; |
44 | | -})(); |
45 | | -`; |
46 | | - |
47 | | -// Inject into MAIN world |
48 | | -const script = document.createElement("script"); |
49 | | -script.textContent = interceptorCode; |
50 | | -document.documentElement.prepend(script); |
51 | | -script.remove(); |
52 | | - |
53 | | -/* ── Listen for intercepted data ──────────────────────────── */ |
| 15 | +/* ── 1. Listen for intercepted data from MAIN world ────────── */ |
54 | 16 |
|
55 | 17 | window.addEventListener("message", (event) => { |
56 | 18 | if (event.data?.type !== "__HACKLM_MIGRATE_GEMINI_INTERCEPT__") return; |
@@ -122,19 +84,33 @@ function getConversationId(): string | null { |
122 | 84 | return match?.[2] ?? null; |
123 | 85 | } |
124 | 86 |
|
125 | | -function injectIntoTextarea(text: string) { |
126 | | - const input = document.querySelector<HTMLElement>( |
127 | | - "[contenteditable='true'], textarea, .ql-editor" |
128 | | - ); |
129 | | - if (!input) return; |
| 87 | +function attachViaFileInput(text: string): boolean { |
| 88 | + const fileInput = document.querySelector<HTMLInputElement>("input[type='file']"); |
| 89 | + if (!fileInput) return false; |
130 | 90 |
|
131 | | - if (input.tagName === "TEXTAREA") { |
132 | | - (input as HTMLTextAreaElement).value = text; |
133 | | - } else { |
134 | | - input.innerText = text; |
135 | | - } |
136 | | - input.dispatchEvent(new Event("input", { bubbles: true })); |
137 | | - input.focus(); |
| 91 | + const file = new File([text], "AI_Chat_Backup_Context_Seed.txt", { |
| 92 | + type: "text/plain", |
| 93 | + lastModified: Date.now(), |
| 94 | + }); |
| 95 | + |
| 96 | + const dt = new DataTransfer(); |
| 97 | + dt.items.add(file); |
| 98 | + fileInput.files = dt.files; |
| 99 | + fileInput.dispatchEvent(new Event("change", { bubbles: true })); |
| 100 | + return true; |
| 101 | +} |
| 102 | + |
| 103 | +function injectIntoTextarea(text: string) { |
| 104 | + if (attachViaFileInput(text)) return; |
| 105 | + |
| 106 | + // File input not yet in DOM – retry for up to 5 s |
| 107 | + let attempts = 0; |
| 108 | + const interval = setInterval(() => { |
| 109 | + attempts++; |
| 110 | + if (attachViaFileInput(text) || attempts > 20) { |
| 111 | + clearInterval(interval); |
| 112 | + } |
| 113 | + }, 250); |
138 | 114 | } |
139 | 115 |
|
140 | 116 | mountWidget( |
|
0 commit comments