feat(export): add batch export feature for all Gemini conversations#235
feat(export): add batch export feature for all Gemini conversations#235chengyongru wants to merge 4 commits intoNagi-ovo:mainfrom
Conversation
Add comprehensive batch export functionality that automatically traverses and exports all conversations as a ZIP file containing individual JSON files. Core Features: - Automatic conversation traversal and sequential loading - Lazy loading trigger to capture complete conversation history - Progress tracking with success/failure reporting - ZIP file generation with timestamped filenames - Reuses proven collectChatPairs() for content extraction UI Changes: - Add "Batch Export" button to export dialog - Include batch export description in dialog UI - Support for all 9 languages (en, zh, zh_CN, zh_TW, ja, fr, es, pt, ar, ru) Technical Implementation: - BatchExportService: Handles automated conversation navigation - DOM fingerprinting for lazy loading detection - Recursive history loading with stabilization checks - Export result tracking with detailed error reporting Files Added: - src/features/export/services/BatchExportService.ts Files Modified: - src/features/export/ui/ExportDialog.ts (batch export button) - src/pages/content/export/index.ts (lazy loading integration) - src/locales/*/messages.json (translations for all languages)
| const warningElement = dialog.querySelector('.gv-export-dialog-warning'); | ||
| if (warningElement) { | ||
| warningElement.after(batchDesc); | ||
| } |
There was a problem hiding this comment.
🟡 Batch export description never inserted due to querying element before it's appended to DOM
The batch export description element is never inserted into the dialog because the code queries for .gv-export-dialog-warning before the warning element is appended to the dialog.
Click to expand
Root Cause
At line 132, the code tries to find the warning element:
const warningElement = dialog.querySelector('.gv-export-dialog-warning');However, the warning element is only appended to dialog later at line 142:
dialog.appendChild(warning);This means warningElement will always be null, and the batchDesc element created at lines 127-129 will never be inserted into the dialog.
Impact
The batch export description text ("Export all conversations as individual JSON files (ZIP)") will never be displayed to users, even though the translation is provided. This is a UI bug that affects user experience but doesn't break functionality.
Expected vs Actual
- Expected: The batch export description should appear in the dialog after the warning message
- Actual: The description is never shown because the insertion fails silently
Recommendation: Move the batch export description insertion logic to after the dialog elements are assembled (after line 144), or directly use the warning variable reference instead of querying for it: warning.after(batchDesc);
Was this helpful? React with 👍 or 👎 to provide feedback.
Fix two bugs identified by Devin AI code review:
1. 🔴 Critical: Stale topNode reference causing incomplete lazy loading
- Root cause: recursivelyLoadHistory reused the same topNode reference
across recursive calls, but Gemini prepends new messages making
the old reference point to a middle element
- Fix: Re-fetch the top user element on each recursive attempt
- Added getTopUserElement() helper method that filters for top-level
user elements (not nested)
- Aligns with the proven pattern in executeExportSequence (index.ts:608)
2. 🟡 UI bug: Batch export description never displayed
- Root cause: querySelector executed before warning element was
appended to DOM, always returning null
- Fix: Move dialog assembly before batch export button creation,
use warning variable directly instead of querying
- Description now correctly appears in export dialog
Impact:
- Long conversations will now export completely instead of truncating
- Users will see the batch export description text in the UI
- No breaking changes to existing functionality
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…lector
Address Devin AI review feedback about duplicate code between BatchExportService
and index.ts. The ConversationCollector object was specifically exported for
BatchExportService to use, but it wasn't being utilized.
Changes:
1. Fixed index.ts recursivelyLoadHistory to re-fetch topNode on each attempt
- Resolves the same stale reference bug we fixed in BatchExportService
- Ensures complete lazy loading for all conversation history
2. Refactored BatchExportService to use ConversationCollector
- Removed 243 lines of duplicate code
- Changed import from collectChatPairs to ConversationCollector
- Replaced triggerLazyLoading() with ConversationCollector.triggerLazyLoad()
- Replaced collectChatPairs() with ConversationCollector.collectCurrentPairs()
- Deleted redundant methods:
* recursivelyLoadHistory()
* getTopUserElement()
* computeFingerprint()
* hashString()
* waitForFingerprintChange()
* waitForAnyElement()
Benefits:
- Single source of truth for lazy loading logic
- Consistent selector management across export features
- Future selector updates only need to be made in one place
- Reduced code from 536 lines to 332 lines (-204 lines)
Impact:
- All export features (single and batch) now share identical lazy loading
behavior, ensuring consistent data collection
- No functional changes - same behavior with less code duplication
|
@chengyongru Hi, 感谢你的 PR!如果你准备好了 code review 请 @ 我一下! |
|
@Nagi-ovo 通过这种方案批量导出的速度过慢,pr暂时不具备实用价值,可以先留着给有需要的人自己 pull下去打包使用。 实际测试中几十条对话还行,上千的话就有点捉急了,我有时间再研究研究能不能直接用接口,可能不是很快。 |
|
这样不如用google takeout导出再做数据清理来的方便了 |
|
@ypwcharles 我试过takeout 只能导出 gem这些prompt啥的,你有测试过如何导出全部对话吗? |
|
@chengyongru @ypwcharles 勾选活动记录的话应该可以导出对话,但我刚尝试了一下,发现导出来的完全就是活动记录里那种碎片化的方式,好乱啊 |
有试过,在活动记录里面选gemini可以导出全部的对话记录。你可以问一下gemini具体怎么操作 |
是的很乱,而且它是单条的对话记录,要写程序清理然后重新分组才是原来的对话记录。不过对话是全的 |
|
@ypwcharles 测试了一下发现活动记录导出的json没有会话信息,是按时间组织的,这就比较恼火了,虽然可以简单按时间窗口做个聚类,或者按机器学习的方法聚类一下,但是没办法做到完美的把所有对话正确组织成和网页一样的顺序。 但是从另一个角度考虑,之所以要把所有数据都拿到本地其实是为了知识管理,可能找个嵌入模型把这些文本转换成向量做聚类,再用时间做个排序,可能比完美恢复对话顺序更有实际意义一点。 |
|
确实是的,我目前的做法是清理后导入obsidian,也许这个是个值得做的产品方向
——————————————————
Charles Yang
***@***.*** ***@***.***>
chengyongru ***@***.***> 于 2026年2月8日周日 01:19写道:
… *chengyongru* left a comment (Nagi-ovo/gemini-voyager#235)
<#235 (comment)>
@ypwcharles <https://github.com/ypwcharles>
测试了一下发现活动记录导出的json没有会话信息,是按时间组织的,这就比较恼火了,虽然可以简单按时间窗口做个聚类,或者按机器学习的方法聚类一下,但是没办法做到完美的把所有对话正确组织成和网页一样的顺序。
但是从另一个角度考虑,之所以要把所有数据都拿到本地其实是为了知识管理,可能找个嵌入模型把这些文本转换成向量做聚类,再用时间做个排序,可能比完美恢复对话顺序更有实际意义一点。
—
Reply to this email directly, view it on GitHub
<#235 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AWAMCLYF6KWZ5PPJP6NY7ZT4KYNB7AVCNFSM6AAAAACTZL6466VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTQNRUHA4DSMRTGQ>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
添加了批量导出功能, 只支持导出json,最终打包为zip保存由于gemini的懒加载,需要手动翻到会话话列表的最下面,否则只能导出已加载的会话列表
导出过程比较慢,会从第一个会话开始一个个往下翻,然后在每个会话内部会自动定位到timeline起点