decant-core 1.0.0
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.
- package/LICENSE +661 -0
- package/README.md +116 -0
- package/ai/base.js +26 -0
- package/ai/chatgpt.js +973 -0
- package/ai/chatgpt_helper.js +137 -0
- package/ai/chatgpt_scroll_collector.js +193 -0
- package/ai/claude.js +410 -0
- package/ai/claude_react_reader.js +59 -0
- package/ai/copilot.js +413 -0
- package/ai/deepseek.js +155 -0
- package/ai/gemini.js +856 -0
- package/ai/gemini_cloud_assist.js +91 -0
- package/ai/google_ai_studio.js +116 -0
- package/ai/google_search_ai.js +114 -0
- package/ai/index.js +34 -0
- package/ai/lumo.js +101 -0
- package/ai/meta.js +110 -0
- package/ai/mistral.js +57 -0
- package/ai/notebooklm.js +140 -0
- package/ai/perplexity.js +85 -0
- package/ai/qwen.js +101 -0
- package/ai/z_ai.js +101 -0
- package/detection/detect-platform.js +100 -0
- package/detection/domains.js +45 -0
- package/lib/turndown.js +803 -0
- package/package.json +56 -0
- package/utils/html-to-markdown.js +374 -0
package/ai/gemini.js
ADDED
|
@@ -0,0 +1,856 @@
|
|
|
1
|
+
import { ChatParser } from './base.js';
|
|
2
|
+
import { convertToMarkdown } from '../utils/html-to-markdown.js';
|
|
3
|
+
|
|
4
|
+
export class GeminiParser extends ChatParser {
|
|
5
|
+
name = 'Gemini';
|
|
6
|
+
isAvailable(url) {
|
|
7
|
+
return url.includes('gemini.google.com');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async parse() {
|
|
11
|
+
console.log('[Gemini Parser] ========== STARTING PARSE() ==========');
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
//1. Title Extraction - Enhanced for Deep Research
|
|
15
|
+
let title = '';
|
|
16
|
+
|
|
17
|
+
const isInsideMessage = (el) => {
|
|
18
|
+
return !!el.closest(
|
|
19
|
+
'user-query, model-response, .conversation-container, message-content, .query-text, .markdown',
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Strategy 1: Using document.title
|
|
24
|
+
console.log('[Gemini Parser] Strategy 1: Looking at document.title...');
|
|
25
|
+
if (document.title) {
|
|
26
|
+
const cleanedDocTitle = document.title
|
|
27
|
+
.replace(/Google/g, '')
|
|
28
|
+
.replace(/Gemini/g, '')
|
|
29
|
+
.replace(/Advanced/g, '')
|
|
30
|
+
.replace(/- /g, '')
|
|
31
|
+
.replace(/—/g, '')
|
|
32
|
+
.trim();
|
|
33
|
+
|
|
34
|
+
const isGeneric =
|
|
35
|
+
!cleanedDocTitle ||
|
|
36
|
+
cleanedDocTitle.toLowerCase() === 'new chat' ||
|
|
37
|
+
cleanedDocTitle.toLowerCase() === 'help' ||
|
|
38
|
+
cleanedDocTitle.toLowerCase() === 'settings';
|
|
39
|
+
|
|
40
|
+
if (cleanedDocTitle && !isGeneric && cleanedDocTitle.length > 2) {
|
|
41
|
+
title = cleanedDocTitle;
|
|
42
|
+
console.log('[Gemini Parser] Title set from Strategy 1 (document.title):', title);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Strategy 2: Looking for active navigation
|
|
47
|
+
if (!title) {
|
|
48
|
+
console.log('[Gemini Parser] Strategy 2: Looking for active navigation...');
|
|
49
|
+
const activeNav = document.querySelector('a[aria-current="page"], .selected');
|
|
50
|
+
if (activeNav) {
|
|
51
|
+
const navText = activeNav.innerText
|
|
52
|
+
.replace(/more_vert/g, '')
|
|
53
|
+
.replace(/\n/g, ' ')
|
|
54
|
+
.trim();
|
|
55
|
+
if (navText && navText.length > 2 && !navText.toLowerCase().includes('new chat')) {
|
|
56
|
+
title = navText;
|
|
57
|
+
console.log('[Gemini Parser] Title set from Strategy 2 (active navigation):', title);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Strategy 3: Check for Deep Research title patterns
|
|
63
|
+
if (!title) {
|
|
64
|
+
console.log('[Gemini Parser] Strategy 3: Looking for Deep Research title patterns...');
|
|
65
|
+
const deepResearchTitle = document.querySelector(
|
|
66
|
+
'h1, .title, .conversation-title, [data-testid="title"]',
|
|
67
|
+
);
|
|
68
|
+
if (deepResearchTitle && !isInsideMessage(deepResearchTitle)) {
|
|
69
|
+
const text = deepResearchTitle.innerText.trim();
|
|
70
|
+
console.log('[Gemini Parser] Found potential title:', text);
|
|
71
|
+
if (
|
|
72
|
+
text.length > 5 &&
|
|
73
|
+
!text.includes('Gemini') &&
|
|
74
|
+
!text.includes('Help') &&
|
|
75
|
+
!text.includes('Settings')
|
|
76
|
+
) {
|
|
77
|
+
title = text;
|
|
78
|
+
console.log('[Gemini Parser] Title set from Strategy 3:', title);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Strategy 4: Look for title in page content (Deep Research reports often have titles in content)
|
|
84
|
+
if (!title) {
|
|
85
|
+
console.log('[Gemini Parser] Strategy 4: Looking for title in page content...');
|
|
86
|
+
const contentTitles = document.querySelectorAll(
|
|
87
|
+
'main h1, main h2, article h1, article h2, .content h1, .content h2',
|
|
88
|
+
);
|
|
89
|
+
console.log('[Gemini Parser] Found content titles:', contentTitles.length);
|
|
90
|
+
for (const el of contentTitles) {
|
|
91
|
+
if (isInsideMessage(el)) continue;
|
|
92
|
+
const text = el.innerText.trim();
|
|
93
|
+
if (
|
|
94
|
+
text.length > 5 &&
|
|
95
|
+
!text.includes('Gemini') &&
|
|
96
|
+
!text.includes('Help') &&
|
|
97
|
+
!text.includes('Settings') &&
|
|
98
|
+
!text.includes('Prompt:') &&
|
|
99
|
+
!text.includes('Response:')
|
|
100
|
+
) {
|
|
101
|
+
title = text;
|
|
102
|
+
console.log('[Gemini Parser] Title set from Strategy 4:', title);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Strategy 5: Top Bar or Sidebar (original logic)
|
|
109
|
+
if (!title) {
|
|
110
|
+
console.log('[Gemini Parser] Strategy 5: Looking for title in top bar/sidebar...');
|
|
111
|
+
const possibleHeaders = document.querySelectorAll(
|
|
112
|
+
'h1, button[aria-haspopup="true"], button[aria-expanded]',
|
|
113
|
+
);
|
|
114
|
+
console.log('[Gemini Parser] Found possible headers:', possibleHeaders.length);
|
|
115
|
+
|
|
116
|
+
for (const el of possibleHeaders) {
|
|
117
|
+
if (isInsideMessage(el)) continue;
|
|
118
|
+
const text = el.innerText.trim();
|
|
119
|
+
if (
|
|
120
|
+
text.length > 5 &&
|
|
121
|
+
!text.includes('Gemini') &&
|
|
122
|
+
!text.includes('Help') &&
|
|
123
|
+
!text.includes('Settings')
|
|
124
|
+
) {
|
|
125
|
+
const rect = el.getBoundingClientRect();
|
|
126
|
+
if (rect.top < 100 && rect.left > 50) {
|
|
127
|
+
title = text;
|
|
128
|
+
console.log('[Gemini Parser] Title set from Strategy 5:', title);
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!title || title.length < 2) {
|
|
136
|
+
title = 'Gemini Conversation';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
console.log('[Gemini Parser] Final title:', title);
|
|
140
|
+
|
|
141
|
+
const messages = [];
|
|
142
|
+
const seenTexts = new Set();
|
|
143
|
+
|
|
144
|
+
console.log('[Gemini Parser] Starting content extraction...');
|
|
145
|
+
let extractionAttempted = false;
|
|
146
|
+
|
|
147
|
+
// 2. Content Extraction - Enhanced for Deep Research
|
|
148
|
+
// Try multiple strategies to extract content
|
|
149
|
+
|
|
150
|
+
// Strategy 1: Original conversation containers
|
|
151
|
+
console.log('[Gemini Parser] Strategy 1: Looking for conversation containers...');
|
|
152
|
+
const conversationContainers = document.querySelectorAll('.conversation-container');
|
|
153
|
+
console.log('[Gemini Parser] Found conversation containers:', conversationContainers.length);
|
|
154
|
+
if (conversationContainers.length > 0) {
|
|
155
|
+
console.log('[Gemini Parser] Processing conversation containers...');
|
|
156
|
+
extractionAttempted = true;
|
|
157
|
+
conversationContainers.forEach((container) => {
|
|
158
|
+
console.log('[Gemini Parser] Processing conversation container...');
|
|
159
|
+
|
|
160
|
+
// First, check for user query
|
|
161
|
+
const userQuery = container.querySelector('user-query');
|
|
162
|
+
if (userQuery) {
|
|
163
|
+
console.log('[Gemini Parser] Found user query...');
|
|
164
|
+
const queryText = userQuery.querySelector('.query-text');
|
|
165
|
+
if (queryText) {
|
|
166
|
+
console.log('[Gemini Parser] Found query text...');
|
|
167
|
+
const clone = queryText.cloneNode(true);
|
|
168
|
+
clone
|
|
169
|
+
.querySelectorAll('.cdk-visually-hidden, [class*="screen-reader"]')
|
|
170
|
+
.forEach((el) => el.remove());
|
|
171
|
+
const userText = clone.innerText.trim();
|
|
172
|
+
if (userText && !seenTexts.has(userText)) {
|
|
173
|
+
seenTexts.add(userText);
|
|
174
|
+
messages.push({
|
|
175
|
+
role: 'User',
|
|
176
|
+
content: userText,
|
|
177
|
+
});
|
|
178
|
+
console.log(
|
|
179
|
+
'[Gemini Parser] Added user message:',
|
|
180
|
+
userText.substring(0, 50) + '...',
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Then, check for model response
|
|
187
|
+
const modelResponse = container.querySelector('model-response');
|
|
188
|
+
if (modelResponse) {
|
|
189
|
+
console.log('[Gemini Parser] Found model response...');
|
|
190
|
+
const messageContent = modelResponse.querySelector('message-content');
|
|
191
|
+
if (messageContent) {
|
|
192
|
+
console.log('[Gemini Parser] Found message content...');
|
|
193
|
+
const markdownDiv = messageContent.querySelector(
|
|
194
|
+
'.markdown.markdown-main-panel, .markdown',
|
|
195
|
+
);
|
|
196
|
+
if (markdownDiv) {
|
|
197
|
+
console.log('[Gemini Parser] Found markdown div...');
|
|
198
|
+
// Clone to avoid modifying the original DOM
|
|
199
|
+
const clone = markdownDiv.cloneNode(true);
|
|
200
|
+
|
|
201
|
+
// Remove UI elements that shouldn't be in the export
|
|
202
|
+
clone
|
|
203
|
+
.querySelectorAll(
|
|
204
|
+
'button, .thoughts-container, .thoughts-wrapper, model-thoughts, .table-footer, .hide-from-message-actions',
|
|
205
|
+
)
|
|
206
|
+
.forEach((el) => el.remove());
|
|
207
|
+
|
|
208
|
+
// Remove response-element wrappers (they contain export buttons)
|
|
209
|
+
clone.querySelectorAll('response-element').forEach((el) => {
|
|
210
|
+
// Keep the table but remove the wrapper
|
|
211
|
+
while (el.firstChild) {
|
|
212
|
+
el.parentNode.insertBefore(el.firstChild, el);
|
|
213
|
+
}
|
|
214
|
+
el.remove();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Convert to markdown
|
|
218
|
+
const text = convertToMarkdown(clone);
|
|
219
|
+
console.log('[Gemini Parser] Converted to markdown, length:', text.length);
|
|
220
|
+
console.log('[Gemini Parser] Full markdown content:');
|
|
221
|
+
console.log(text);
|
|
222
|
+
console.log('[Gemini Parser] End of markdown content');
|
|
223
|
+
|
|
224
|
+
if (text && text.trim() && !seenTexts.has(text.trim())) {
|
|
225
|
+
seenTexts.add(text.trim());
|
|
226
|
+
messages.push({
|
|
227
|
+
role: 'Model',
|
|
228
|
+
content: text.trim(),
|
|
229
|
+
});
|
|
230
|
+
console.log(
|
|
231
|
+
'[Gemini Parser] Added model message:',
|
|
232
|
+
text.substring(0, 50) + '...',
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
console.log(
|
|
237
|
+
'[Gemini Parser] No markdown div found, trying comprehensive content extraction...',
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
// Strategy 1: Look for content in nested elements within message-content
|
|
241
|
+
const nestedSelectors = [
|
|
242
|
+
'div[class*="content"]',
|
|
243
|
+
'div[class*="research"]',
|
|
244
|
+
'div[class*="report"]',
|
|
245
|
+
'div[class*="analysis"]',
|
|
246
|
+
'div[class*="section"]',
|
|
247
|
+
'div[class*="paragraph"]',
|
|
248
|
+
'p',
|
|
249
|
+
'article',
|
|
250
|
+
'section',
|
|
251
|
+
];
|
|
252
|
+
|
|
253
|
+
let foundContent = false;
|
|
254
|
+
for (const selector of nestedSelectors) {
|
|
255
|
+
const nestedElements = messageContent.querySelectorAll(selector);
|
|
256
|
+
console.log(
|
|
257
|
+
'[Gemini Parser] Looking for nested elements with selector:',
|
|
258
|
+
selector,
|
|
259
|
+
'found:',
|
|
260
|
+
nestedElements.length,
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
nestedElements.forEach((element) => {
|
|
264
|
+
const text = element.innerText.trim();
|
|
265
|
+
if (text.length > 100) {
|
|
266
|
+
console.log('[Gemini Parser] Found nested content, length:', text.length);
|
|
267
|
+
console.log(
|
|
268
|
+
'[Gemini Parser] Nested content preview:',
|
|
269
|
+
text.substring(0, 200) + '...',
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
const isDeepResearch =
|
|
273
|
+
text.includes('research') ||
|
|
274
|
+
text.includes('analysis') ||
|
|
275
|
+
text.includes('findings') ||
|
|
276
|
+
text.includes('cost') ||
|
|
277
|
+
text.includes('sweetener') ||
|
|
278
|
+
text.includes('projection') ||
|
|
279
|
+
text.includes('historical') ||
|
|
280
|
+
text.includes('economic') ||
|
|
281
|
+
text.includes('market') ||
|
|
282
|
+
text.includes('price') ||
|
|
283
|
+
text.includes('industry');
|
|
284
|
+
|
|
285
|
+
console.log('[Gemini Parser] Is Deep Research content:', isDeepResearch);
|
|
286
|
+
|
|
287
|
+
if (!seenTexts.has(text)) {
|
|
288
|
+
seenTexts.add(text);
|
|
289
|
+
messages.push({
|
|
290
|
+
role: 'Model',
|
|
291
|
+
content: text,
|
|
292
|
+
});
|
|
293
|
+
console.log(
|
|
294
|
+
'[Gemini Parser] Added nested content, Deep Research:',
|
|
295
|
+
isDeepResearch,
|
|
296
|
+
);
|
|
297
|
+
foundContent = true;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
if (foundContent) break;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Strategy 2: Look for content in parent/sibling elements of model-response
|
|
306
|
+
if (!foundContent) {
|
|
307
|
+
console.log('[Gemini Parser] Trying parent/sibling element extraction...');
|
|
308
|
+
const parentContainer = modelResponse.parentElement;
|
|
309
|
+
if (parentContainer) {
|
|
310
|
+
const siblings = parentContainer.children;
|
|
311
|
+
console.log('[Gemini Parser] Checking siblings, count:', siblings.length);
|
|
312
|
+
|
|
313
|
+
Array.from(siblings).forEach((sibling) => {
|
|
314
|
+
if (sibling !== modelResponse) {
|
|
315
|
+
const text = sibling.innerText.trim();
|
|
316
|
+
if (text.length > 200) {
|
|
317
|
+
console.log(
|
|
318
|
+
'[Gemini Parser] Found sibling content, length:',
|
|
319
|
+
text.length,
|
|
320
|
+
);
|
|
321
|
+
console.log(
|
|
322
|
+
'[Gemini Parser] Sibling content preview:',
|
|
323
|
+
text.substring(0, 200) + '...',
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
if (!seenTexts.has(text)) {
|
|
327
|
+
seenTexts.add(text);
|
|
328
|
+
messages.push({
|
|
329
|
+
role: 'Model',
|
|
330
|
+
content: text,
|
|
331
|
+
});
|
|
332
|
+
console.log('[Gemini Parser] Added sibling content');
|
|
333
|
+
foundContent = true;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Strategy 3: Look for content in the entire conversation container (outside model-response)
|
|
342
|
+
if (!foundContent) {
|
|
343
|
+
console.log('[Gemini Parser] Trying full container content extraction...');
|
|
344
|
+
const containerText = container.innerText.trim();
|
|
345
|
+
console.log('[Gemini Parser] Full container text length:', containerText.length);
|
|
346
|
+
|
|
347
|
+
if (containerText.length > 500) {
|
|
348
|
+
console.log(
|
|
349
|
+
'[Gemini Parser] Full container content preview:',
|
|
350
|
+
containerText.substring(0, 200) + '...',
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
if (!seenTexts.has(containerText)) {
|
|
354
|
+
seenTexts.add(containerText);
|
|
355
|
+
messages.push({
|
|
356
|
+
role: 'Model',
|
|
357
|
+
content: containerText,
|
|
358
|
+
});
|
|
359
|
+
console.log('[Gemini Parser] Added full container content');
|
|
360
|
+
foundContent = true;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Strategy 4: Last resort - get all text from message-content
|
|
366
|
+
if (!foundContent) {
|
|
367
|
+
const allText = messageContent.innerText.trim();
|
|
368
|
+
console.log('[Gemini Parser] Last resort text length:', allText.length);
|
|
369
|
+
console.log(
|
|
370
|
+
'[Gemini Parser] Last resort text preview:',
|
|
371
|
+
allText.substring(0, 200) + '...',
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
if (allText && allText.length > 50) {
|
|
375
|
+
// Check if this looks like Deep Research content
|
|
376
|
+
const isDeepResearch =
|
|
377
|
+
allText.includes('research') ||
|
|
378
|
+
allText.includes('analysis') ||
|
|
379
|
+
allText.includes('findings') ||
|
|
380
|
+
allText.includes('cost') ||
|
|
381
|
+
allText.includes('sweetener') ||
|
|
382
|
+
allText.includes('projection') ||
|
|
383
|
+
allText.includes('historical');
|
|
384
|
+
|
|
385
|
+
console.log('[Gemini Parser] Is Deep Research content:', isDeepResearch);
|
|
386
|
+
|
|
387
|
+
if (!seenTexts.has(allText)) {
|
|
388
|
+
seenTexts.add(allText);
|
|
389
|
+
messages.push({
|
|
390
|
+
role: 'Model',
|
|
391
|
+
content: allText,
|
|
392
|
+
});
|
|
393
|
+
console.log(
|
|
394
|
+
'[Gemini Parser] Added last resort model message, Deep Research:',
|
|
395
|
+
isDeepResearch,
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
} else {
|
|
402
|
+
console.log(
|
|
403
|
+
'[Gemini Parser] No message-content found, trying direct model-response text...',
|
|
404
|
+
);
|
|
405
|
+
// Fallback: get text directly from model-response
|
|
406
|
+
const directText = modelResponse.innerText.trim();
|
|
407
|
+
console.log('[Gemini Parser] Direct model response text length:', directText.length);
|
|
408
|
+
|
|
409
|
+
if (directText && directText.length > 50) {
|
|
410
|
+
const isDeepResearch =
|
|
411
|
+
directText.includes('research') ||
|
|
412
|
+
directText.includes('analysis') ||
|
|
413
|
+
directText.includes('findings') ||
|
|
414
|
+
directText.includes('cost') ||
|
|
415
|
+
directText.includes('sweetener') ||
|
|
416
|
+
directText.includes('projection') ||
|
|
417
|
+
directText.includes('historical');
|
|
418
|
+
|
|
419
|
+
console.log('[Gemini Parser] Direct text is Deep Research:', isDeepResearch);
|
|
420
|
+
|
|
421
|
+
if (!seenTexts.has(directText)) {
|
|
422
|
+
seenTexts.add(directText);
|
|
423
|
+
messages.push({
|
|
424
|
+
role: 'Model',
|
|
425
|
+
content: directText,
|
|
426
|
+
});
|
|
427
|
+
console.log(
|
|
428
|
+
'[Gemini Parser] Added direct model message, Deep Research:',
|
|
429
|
+
isDeepResearch,
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Strategy 2: Deep Research content extraction
|
|
439
|
+
if (!extractionAttempted) {
|
|
440
|
+
console.log(
|
|
441
|
+
'[Gemini Parser] Strategy 2: No conversation containers found, trying Deep Research panel...',
|
|
442
|
+
);
|
|
443
|
+
extractionAttempted = true;
|
|
444
|
+
|
|
445
|
+
// First try Deep Research immersive panel structure
|
|
446
|
+
console.log('[Gemini Parser] Looking for deep-research-immersive-panel...');
|
|
447
|
+
const deepResearchPanel = document.querySelector('deep-research-immersive-panel');
|
|
448
|
+
console.log('[Gemini Parser] Deep Research panel found:', !!deepResearchPanel);
|
|
449
|
+
|
|
450
|
+
if (deepResearchPanel) {
|
|
451
|
+
console.log('[Gemini Parser] Processing Deep Research panel...');
|
|
452
|
+
|
|
453
|
+
// Extract title from panel
|
|
454
|
+
const panelTitle = deepResearchPanel.querySelector('.title-text, h2, h1');
|
|
455
|
+
if (panelTitle && !title) {
|
|
456
|
+
const titleText = panelTitle.innerText.trim();
|
|
457
|
+
console.log('[Gemini Parser] Found panel title:', titleText);
|
|
458
|
+
if (titleText.length > 5 && !titleText.includes('Gemini')) {
|
|
459
|
+
title = titleText;
|
|
460
|
+
console.log('[Gemini Parser] Title updated from panel:', title);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Extract content from panel
|
|
465
|
+
console.log('[Gemini Parser] Calling extractDeepResearchPanelContent...');
|
|
466
|
+
const panelContent = this.extractDeepResearchPanelContent(deepResearchPanel);
|
|
467
|
+
console.log('[Gemini Parser] Panel content extracted, sections:', panelContent.length);
|
|
468
|
+
|
|
469
|
+
if (panelContent.length > 0) {
|
|
470
|
+
panelContent.forEach((section) => {
|
|
471
|
+
if (section.content && !seenTexts.has(section.content)) {
|
|
472
|
+
seenTexts.add(section.content);
|
|
473
|
+
messages.push({
|
|
474
|
+
role: section.role,
|
|
475
|
+
content: section.content,
|
|
476
|
+
});
|
|
477
|
+
console.log(
|
|
478
|
+
'[Gemini Parser] Added panel section, role:',
|
|
479
|
+
section.role,
|
|
480
|
+
'length:',
|
|
481
|
+
section.content.length,
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
} else {
|
|
486
|
+
console.log('[Gemini Parser] No panel content found, trying fallback...');
|
|
487
|
+
}
|
|
488
|
+
} else {
|
|
489
|
+
console.log('[Gemini Parser] No Deep Research panel found');
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Fallback: Look for content in main, article, or content areas
|
|
493
|
+
if (messages.length === 0) {
|
|
494
|
+
console.log('[Gemini Parser] Strategy 3: Trying fallback content extraction...');
|
|
495
|
+
const contentSelectors = [
|
|
496
|
+
'main',
|
|
497
|
+
'article',
|
|
498
|
+
'.content',
|
|
499
|
+
'.main-content',
|
|
500
|
+
'[role="main"]',
|
|
501
|
+
'.conversation-content',
|
|
502
|
+
'.chat-content',
|
|
503
|
+
'.message-content',
|
|
504
|
+
];
|
|
505
|
+
|
|
506
|
+
let contentFound = false;
|
|
507
|
+
|
|
508
|
+
for (const selector of contentSelectors) {
|
|
509
|
+
console.log('[Gemini Parser] Trying selector:', selector);
|
|
510
|
+
const contentElement = document.querySelector(selector);
|
|
511
|
+
if (contentElement) {
|
|
512
|
+
console.log('[Gemini Parser] Found content element:', !!contentElement);
|
|
513
|
+
// Extract all text content from the main content area
|
|
514
|
+
const textContent = contentElement.innerText.trim();
|
|
515
|
+
console.log('[Gemini Parser] Text content length:', textContent.length);
|
|
516
|
+
|
|
517
|
+
if (textContent && textContent.length > 100) {
|
|
518
|
+
// Try to identify user prompts and responses
|
|
519
|
+
const sections = this.extractDeepResearchSections(contentElement);
|
|
520
|
+
console.log('[Gemini Parser] Extracted sections:', sections.length);
|
|
521
|
+
|
|
522
|
+
if (sections.length > 0) {
|
|
523
|
+
sections.forEach((section) => {
|
|
524
|
+
if (section.content && !seenTexts.has(section.content)) {
|
|
525
|
+
seenTexts.add(section.content);
|
|
526
|
+
messages.push({
|
|
527
|
+
role: section.role,
|
|
528
|
+
content: section.content,
|
|
529
|
+
});
|
|
530
|
+
console.log('[Gemini Parser] Added fallback section, role:', section.role);
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
contentFound = true;
|
|
534
|
+
break;
|
|
535
|
+
} else {
|
|
536
|
+
// If we can't parse sections, treat the whole content as a response
|
|
537
|
+
console.log('[Gemini Parser] Treating whole content as response...');
|
|
538
|
+
const markdown = convertToMarkdown(contentElement);
|
|
539
|
+
if (markdown && markdown.trim() && !seenTexts.has(markdown.trim())) {
|
|
540
|
+
seenTexts.add(markdown.trim());
|
|
541
|
+
messages.push({
|
|
542
|
+
role: 'Model',
|
|
543
|
+
content: markdown.trim(),
|
|
544
|
+
});
|
|
545
|
+
console.log('[Gemini Parser] Added fallback message');
|
|
546
|
+
contentFound = true;
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Strategy 3: Fallback - look for any meaningful content
|
|
555
|
+
if (!contentFound) {
|
|
556
|
+
console.log('[Gemini Parser] Strategy 4: Final fallback - extracting body content...');
|
|
557
|
+
const bodyContent = document.body.innerText.trim();
|
|
558
|
+
console.log('[Gemini Parser] Body content length:', bodyContent.length);
|
|
559
|
+
|
|
560
|
+
if (bodyContent && bodyContent.length > 200) {
|
|
561
|
+
// Try to extract structured content from body
|
|
562
|
+
const sections = this.extractDeepResearchSections(document.body);
|
|
563
|
+
console.log('[Gemini Parser] Body sections extracted:', sections.length);
|
|
564
|
+
|
|
565
|
+
if (sections.length > 0) {
|
|
566
|
+
sections.forEach((section) => {
|
|
567
|
+
if (section.content && !seenTexts.has(section.content)) {
|
|
568
|
+
seenTexts.add(section.content);
|
|
569
|
+
messages.push({
|
|
570
|
+
role: section.role,
|
|
571
|
+
content: section.content,
|
|
572
|
+
});
|
|
573
|
+
console.log('[Gemini Parser] Added body section, role:', section.role);
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
} else {
|
|
577
|
+
// Last resort - treat as single response
|
|
578
|
+
console.log('[Gemini Parser] Last resort - treating as single response...');
|
|
579
|
+
messages.push({
|
|
580
|
+
role: 'Model',
|
|
581
|
+
content: bodyContent,
|
|
582
|
+
});
|
|
583
|
+
console.log('[Gemini Parser] Added last resort message');
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
console.log('[Gemini Parser] Total messages extracted:', messages.length);
|
|
591
|
+
console.log('[Gemini Parser] ========== FINISHING PARSE() ==========');
|
|
592
|
+
|
|
593
|
+
const currentUrl =
|
|
594
|
+
typeof window !== 'undefined' && window.location ? window.location.href || '' : '';
|
|
595
|
+
const metadata = {
|
|
596
|
+
Source: 'Gemini',
|
|
597
|
+
Date: new Date().toLocaleString(),
|
|
598
|
+
Link: currentUrl,
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
return {
|
|
602
|
+
title: title,
|
|
603
|
+
messages: messages,
|
|
604
|
+
url: currentUrl,
|
|
605
|
+
metadata: metadata,
|
|
606
|
+
};
|
|
607
|
+
} catch (error) {
|
|
608
|
+
console.error('[Gemini Parser] Error during parsing:', error);
|
|
609
|
+
const currentUrl =
|
|
610
|
+
typeof window !== 'undefined' && window.location ? window.location.href || '' : '';
|
|
611
|
+
return {
|
|
612
|
+
title: 'Gemini Conversation',
|
|
613
|
+
messages: [],
|
|
614
|
+
url: currentUrl,
|
|
615
|
+
metadata: {
|
|
616
|
+
Source: 'Gemini',
|
|
617
|
+
Date: new Date().toLocaleString(),
|
|
618
|
+
Link: currentUrl,
|
|
619
|
+
},
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Helper method to extract sections from Deep Research content
|
|
625
|
+
extractDeepResearchSections(contentElement) {
|
|
626
|
+
console.log('[Gemini Parser] Extracting Deep Research sections...');
|
|
627
|
+
const sections = [];
|
|
628
|
+
const text = contentElement.innerText || '';
|
|
629
|
+
|
|
630
|
+
// Look for common Deep Research patterns
|
|
631
|
+
const patterns = [
|
|
632
|
+
// Pattern 1: "Prompt:" and "Response:" sections
|
|
633
|
+
{
|
|
634
|
+
promptRegex:
|
|
635
|
+
/(?:Prompt|You said)[:\s]*\n*([\s\S]*?)(?=\n\s*(?:Response|I've completed|Generating|Start research)|$)/i,
|
|
636
|
+
responseRegex:
|
|
637
|
+
/(?:Response|I've completed|Generating|Start research)[:\s]*\n*([\s\S]*?)(?=\n\s*(?:Prompt|You said)|$)/i,
|
|
638
|
+
},
|
|
639
|
+
// Pattern 2: Question/Answer format
|
|
640
|
+
{
|
|
641
|
+
promptRegex: /(?:Question|Q)[:\s]*\n*([\s\S]*?)(?=\n\s*(?:Answer|A|Response)|$)/i,
|
|
642
|
+
responseRegex: /(?:Answer|A|Response)[:\s]*\n*([\s\S]*?)(?=\n\s*(?:Question|Q)|$)/i,
|
|
643
|
+
},
|
|
644
|
+
// Pattern 3: Look for research plan and results
|
|
645
|
+
{
|
|
646
|
+
promptRegex:
|
|
647
|
+
/(?:Research plan|Research query|What is|How has|What's the projection)[:\s]*\n*([\s\S]*?)(?=\n\s*(?:I've completed|Research|Analysis|Results)|$)/i,
|
|
648
|
+
responseRegex:
|
|
649
|
+
/(?:I've completed|Research|Analysis|Results|Findings)[:\s]*\n*([\s\S]*?)(?=\n\s*(?:Research plan|Research query|What is|How has)|$)/i,
|
|
650
|
+
},
|
|
651
|
+
];
|
|
652
|
+
|
|
653
|
+
// Try each pattern
|
|
654
|
+
for (const pattern of patterns) {
|
|
655
|
+
console.log('[Gemini Parser] Trying pattern...');
|
|
656
|
+
const promptMatches = text.match(pattern.promptRegex);
|
|
657
|
+
const responseMatches = text.match(pattern.responseRegex);
|
|
658
|
+
|
|
659
|
+
if (promptMatches && promptMatches[1]) {
|
|
660
|
+
const promptContent = promptMatches[1].trim();
|
|
661
|
+
console.log('[Gemini Parser] Found prompt content, length:', promptContent.length);
|
|
662
|
+
if (promptContent.length > 20) {
|
|
663
|
+
sections.push({
|
|
664
|
+
role: 'User',
|
|
665
|
+
content: promptContent,
|
|
666
|
+
});
|
|
667
|
+
console.log('[Gemini Parser] Added prompt section');
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (responseMatches && responseMatches[1]) {
|
|
672
|
+
const responseContent = responseMatches[1].trim();
|
|
673
|
+
console.log('[Gemini Parser] Found response content, length:', responseContent.length);
|
|
674
|
+
if (responseContent.length > 50) {
|
|
675
|
+
sections.push({
|
|
676
|
+
role: 'Model',
|
|
677
|
+
content: responseContent,
|
|
678
|
+
});
|
|
679
|
+
console.log('[Gemini Parser] Added response section');
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// If we found meaningful sections, stop trying other patterns
|
|
684
|
+
if (sections.length > 0) {
|
|
685
|
+
console.log('[Gemini Parser] Found sections using pattern matching');
|
|
686
|
+
return sections;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// If no structured sections found, try to extract based on HTML structure
|
|
691
|
+
console.log('[Gemini Parser] Trying HTML structure extraction...');
|
|
692
|
+
const userElements = contentElement.querySelectorAll(
|
|
693
|
+
'.user-query, .prompt, .question, [data-role="user"]',
|
|
694
|
+
);
|
|
695
|
+
console.log('[Gemini Parser] Found user elements:', userElements.length);
|
|
696
|
+
userElements.forEach((el) => {
|
|
697
|
+
const clone = el.cloneNode(true);
|
|
698
|
+
clone
|
|
699
|
+
.querySelectorAll('.cdk-visually-hidden, [class*="screen-reader"]')
|
|
700
|
+
.forEach((subEl) => subEl.remove());
|
|
701
|
+
const content = clone.innerText.trim();
|
|
702
|
+
if (content.length > 20) {
|
|
703
|
+
sections.push({
|
|
704
|
+
role: 'User',
|
|
705
|
+
content: content,
|
|
706
|
+
});
|
|
707
|
+
console.log('[Gemini Parser] Added user element from HTML structure');
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
// Look for elements that might contain responses
|
|
712
|
+
const responseElements = contentElement.querySelectorAll(
|
|
713
|
+
'.model-response, .response, .answer, [data-role="model"], .research-content',
|
|
714
|
+
);
|
|
715
|
+
console.log('[Gemini Parser] Found response elements:', responseElements.length);
|
|
716
|
+
responseElements.forEach((el) => {
|
|
717
|
+
const content = el.innerText.trim();
|
|
718
|
+
if (content.length > 50) {
|
|
719
|
+
sections.push({
|
|
720
|
+
role: 'Model',
|
|
721
|
+
content: content,
|
|
722
|
+
});
|
|
723
|
+
console.log('[Gemini Parser] Added response element from HTML structure');
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
// If still no sections, try to split by common delimiters
|
|
728
|
+
if (sections.length === 0) {
|
|
729
|
+
console.log('[Gemini Parser] Trying delimiter splitting...');
|
|
730
|
+
const delimiterPatterns = [
|
|
731
|
+
/\n\s*You said\s*\n/i,
|
|
732
|
+
/\n\s*Response\s*\n/i,
|
|
733
|
+
/\n\s*Prompt\s*\n/i,
|
|
734
|
+
/\n\s*I've completed\s*\n/i,
|
|
735
|
+
];
|
|
736
|
+
|
|
737
|
+
let parts = [text];
|
|
738
|
+
delimiterPatterns.forEach((pattern) => {
|
|
739
|
+
parts = parts.flatMap((part) => part.split(pattern));
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
parts.forEach((part, index) => {
|
|
743
|
+
const trimmedPart = part.trim();
|
|
744
|
+
if (trimmedPart.length > 50) {
|
|
745
|
+
// Alternate between User and Model roles
|
|
746
|
+
const role = index % 2 === 0 ? 'User' : 'Model';
|
|
747
|
+
sections.push({
|
|
748
|
+
role: role,
|
|
749
|
+
content: trimmedPart,
|
|
750
|
+
});
|
|
751
|
+
console.log('[Gemini Parser] Added delimiter section, role:', role);
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
console.log('[Gemini Parser] Total sections extracted:', sections.length);
|
|
757
|
+
return sections;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// Helper method to extract content from Deep Research immersive panel
|
|
761
|
+
extractDeepResearchPanelContent(panelElement) {
|
|
762
|
+
console.log('[Gemini Parser] Looking for Deep Research panel content...');
|
|
763
|
+
const sections = [];
|
|
764
|
+
|
|
765
|
+
console.log('[Gemini Parser] Panel element found:', !!panelElement);
|
|
766
|
+
console.log(
|
|
767
|
+
'[Gemini Parser] Panel innerText length:',
|
|
768
|
+
panelElement.innerText ? panelElement.innerText.length : 0,
|
|
769
|
+
);
|
|
770
|
+
|
|
771
|
+
try {
|
|
772
|
+
// Look for content within panel
|
|
773
|
+
const contentSelectors = [
|
|
774
|
+
'.markdown',
|
|
775
|
+
'.content',
|
|
776
|
+
'.research-content',
|
|
777
|
+
'.panel-content',
|
|
778
|
+
'div[class*="content"]',
|
|
779
|
+
'div[class*="markdown"]',
|
|
780
|
+
'div[class*="research"]',
|
|
781
|
+
];
|
|
782
|
+
|
|
783
|
+
for (const selector of contentSelectors) {
|
|
784
|
+
console.log('[Gemini Parser] Trying selector:', selector);
|
|
785
|
+
const contentElements = panelElement.querySelectorAll(selector);
|
|
786
|
+
console.log('[Gemini Parser] Found elements:', contentElements.length);
|
|
787
|
+
contentElements.forEach((element) => {
|
|
788
|
+
const text = element.innerText.trim();
|
|
789
|
+
if (text.length > 100) {
|
|
790
|
+
sections.push({
|
|
791
|
+
role: 'Model',
|
|
792
|
+
content: text,
|
|
793
|
+
});
|
|
794
|
+
console.log('[Gemini Parser] Added panel content via selector:', selector);
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// If no structured content found, extract all text from panel
|
|
800
|
+
if (sections.length === 0) {
|
|
801
|
+
console.log('[Gemini Parser] No structured content found, extracting all panel text...');
|
|
802
|
+
const panelText = panelElement.innerText.trim();
|
|
803
|
+
console.log('[Gemini Parser] Panel text length:', panelText.length);
|
|
804
|
+
if (panelText.length > 200) {
|
|
805
|
+
// Try to split into logical sections
|
|
806
|
+
const parts = this.splitIntoSections(panelText);
|
|
807
|
+
console.log('[Gemini Parser] Split into parts:', parts.length);
|
|
808
|
+
parts.forEach((part) => {
|
|
809
|
+
if (part.length > 50) {
|
|
810
|
+
sections.push({
|
|
811
|
+
role: 'Model',
|
|
812
|
+
content: part,
|
|
813
|
+
});
|
|
814
|
+
console.log('[Gemini Parser] Added panel text part');
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
} catch (error) {
|
|
820
|
+
console.error('[Gemini Parser] Error extracting panel content:', error);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
console.log('[Gemini Parser] Panel content extraction complete, sections:', sections.length);
|
|
824
|
+
return sections;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// Helper method to split text into logical sections
|
|
828
|
+
splitIntoSections(text) {
|
|
829
|
+
console.log('[Gemini Parser] Splitting text into sections...');
|
|
830
|
+
const sections = [];
|
|
831
|
+
|
|
832
|
+
// Try to split by common delimiters
|
|
833
|
+
const delimiters = [
|
|
834
|
+
/\n\n+/g, // Double newlines
|
|
835
|
+
/\n(?=[A-Z])/g, // Newline followed by capital letter
|
|
836
|
+
/\.\s+/g, // Period followed by space
|
|
837
|
+
];
|
|
838
|
+
|
|
839
|
+
let parts = [text];
|
|
840
|
+
delimiters.forEach((delimiter) => {
|
|
841
|
+
parts = parts.flatMap((part) => part.split(delimiter));
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
// Filter and clean sections
|
|
845
|
+
parts.forEach((part) => {
|
|
846
|
+
const cleaned = part.trim();
|
|
847
|
+
if (cleaned.length > 50 && !cleaned.match(/^\d+$/)) {
|
|
848
|
+
sections.push(cleaned);
|
|
849
|
+
console.log('[Gemini Parser] Added split section, length:', cleaned.length);
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
console.log('[Gemini Parser] Text splitting complete, sections:', sections.length);
|
|
854
|
+
return sections;
|
|
855
|
+
}
|
|
856
|
+
}
|