Full Code of Nagi-ovo/gemini-voyager for AI

main 8789965cd6fb cached
648 files
3.6 MB
982.6k tokens
2132 symbols
1 requests
Download .txt
Showing preview only (3,915K chars total). Download the full file or copy to clipboard to get everything.
Repository: Nagi-ovo/gemini-voyager
Branch: main
Commit: 8789965cd6fb
Files: 648
Total size: 3.6 MB

Directory structure:
gitextract_g57kkec9/

├── .claude/
│   ├── rules/
│   │   ├── content-scripts.md
│   │   ├── high-complexity.md
│   │   ├── i18n.md
│   │   └── typescript.md
│   ├── settings.json
│   └── skills/
│       └── safari-release/
│           └── SKILL.md
├── .editorconfig
├── .entire/
│   ├── .gitignore
│   └── settings.json
├── .gitattributes
├── .github/
│   ├── CONTRIBUTING.md
│   ├── CONTRIBUTING_ES.md
│   ├── CONTRIBUTING_FR.md
│   ├── CONTRIBUTING_JA.md
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   └── feature_request.yml
│   ├── README_AR.md
│   ├── README_ES.md
│   ├── README_FR.md
│   ├── README_JA.md
│   ├── README_KO.md
│   ├── README_PT.md
│   ├── README_RU.md
│   ├── README_ZH.md
│   ├── README_ZH_TW.md
│   ├── RELEASE_TEMPLATE.md
│   ├── docs/
│   │   ├── CHANGELOG.md
│   │   ├── IMPORT_EXPORT_GUIDE.md
│   │   ├── IMPORT_EXPORT_GUIDE_ZH.md
│   │   └── safari/
│   │       ├── INSTALLATION.md
│   │       └── INSTALLATION_ZH.md
│   ├── pull_request_template.md
│   └── workflows/
│       ├── ci.yml
│       ├── deploy-docs.yml
│       ├── issue-claim.yml
│       ├── issue-validator.yml
│       ├── release.yml
│       ├── sponsors.yml
│       └── stale.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── CLAUDE.md
├── CNAME
├── LICENSE
├── README.md
├── commitlint.config.cjs
├── custom-vite-plugins.ts
├── docs/
│   ├── .vitepress/
│   │   ├── config.mts
│   │   └── theme/
│   │       ├── components/
│   │       │   ├── HomeAskAI.vue
│   │       │   ├── HomeReviews.vue
│   │       │   ├── HomeTeaser.vue
│   │       │   ├── README.md
│   │       │   └── SafariDownloadLink.vue
│   │       ├── index.ts
│   │       └── style.css
│   ├── ar/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   ├── en/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   ├── es/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   ├── fr/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   ├── guide/
│   │   ├── batch-delete.md
│   │   ├── cloud-sync.md
│   │   ├── community.md
│   │   ├── context-sync.md
│   │   ├── deep-research.md
│   │   ├── default-model.md
│   │   ├── export.md
│   │   ├── folders.md
│   │   ├── fork.md
│   │   ├── formula-copy.md
│   │   ├── getting-started.md
│   │   ├── input-collapse.md
│   │   ├── installation.md
│   │   ├── markdown-fix.md
│   │   ├── mermaid.md
│   │   ├── nanobanana.md
│   │   ├── prevent-auto-scroll.md
│   │   ├── prompts.md
│   │   ├── quote-reply.md
│   │   ├── recents-hider.md
│   │   ├── settings.md
│   │   ├── sidebar-auto-hide.md
│   │   ├── sidebar.md
│   │   ├── sponsor.md
│   │   ├── tab-title.md
│   │   └── timeline.md
│   ├── index.md
│   ├── ja/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   ├── ko/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   ├── privacy.md
│   ├── pt/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   ├── public/
│   │   └── google79cf501ea29c5eb1.html
│   ├── ru/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   └── zh_TW/
│       ├── guide/
│       │   ├── batch-delete.md
│       │   ├── cloud-sync.md
│       │   ├── community.md
│       │   ├── context-sync.md
│       │   ├── deep-research.md
│       │   ├── default-model.md
│       │   ├── export.md
│       │   ├── folders.md
│       │   ├── fork.md
│       │   ├── formula-copy.md
│       │   ├── getting-started.md
│       │   ├── input-collapse.md
│       │   ├── installation.md
│       │   ├── markdown-fix.md
│       │   ├── mermaid.md
│       │   ├── nanobanana.md
│       │   ├── prevent-auto-scroll.md
│       │   ├── prompts.md
│       │   ├── quote-reply.md
│       │   ├── recents-hider.md
│       │   ├── settings.md
│       │   ├── sidebar-auto-hide.md
│       │   ├── sidebar.md
│       │   ├── sponsor.md
│       │   ├── tab-title.md
│       │   └── timeline.md
│       ├── index.md
│       └── privacy.md
├── eslint.config.js
├── manifest.dev.json
├── manifest.json
├── nodemon.chrome.json
├── nodemon.firefox.json
├── nodemon.safari.json
├── package.json
├── public/
│   ├── contentStyle.css
│   ├── fetchInterceptor.js
│   ├── katex-config.js
│   └── prevent-auto-scroll.js
├── safari/
│   ├── App/
│   │   └── SafariWebExtensionHandler.swift
│   ├── Models/
│   │   ├── SafariMessage.swift
│   │   └── voyager-v1.3.6.dmg
│   ├── README.md
│   ├── README_ZH.md
│   └── Resources/
│       └── example-native-messaging.js
├── scripts/
│   ├── build-edge.js
│   ├── build-safari.sh
│   ├── bump-version.js
│   ├── generate-sponsors.cjs
│   └── launch-chrome.cjs
├── sponsorkit/
│   └── sponsors.json
├── src/
│   ├── assets/
│   │   └── styles/
│   │       └── tailwind.css
│   ├── components/
│   │   ├── DarkModeToggle.tsx
│   │   ├── LanguageSwitcher.tsx
│   │   └── ui/
│   │       ├── button.tsx
│   │       ├── card.tsx
│   │       ├── label.tsx
│   │       ├── select.tsx
│   │       ├── slider.tsx
│   │       └── switch.tsx
│   ├── contexts/
│   │   └── LanguageContext.tsx
│   ├── core/
│   │   ├── errors/
│   │   │   └── AppError.ts
│   │   ├── index.ts
│   │   ├── services/
│   │   │   ├── AccountIsolationService.ts
│   │   │   ├── DOMService.ts
│   │   │   ├── DataBackupService.ts
│   │   │   ├── GoogleDriveSyncService.ts
│   │   │   ├── KeyboardShortcutService.ts
│   │   │   ├── LoggerService.ts
│   │   │   ├── StorageMonitor.ts
│   │   │   ├── StorageService.ts
│   │   │   └── __tests__/
│   │   │       ├── AccountIsolationService.test.ts
│   │   │       ├── GoogleDriveSyncService.test.ts
│   │   │       └── StorageService.test.ts
│   │   ├── types/
│   │   │   ├── common.ts
│   │   │   ├── folder.ts
│   │   │   ├── keyboardShortcut.ts
│   │   │   ├── sync.ts
│   │   │   └── timeline.ts
│   │   └── utils/
│   │       ├── __tests__/
│   │       │   ├── browser.test.ts
│   │       │   ├── concurrency.test.ts
│   │       │   ├── extensionContext.test.ts
│   │       │   ├── gemini.test.ts
│   │       │   ├── rtl.test.ts
│   │       │   ├── updateReminder.test.ts
│   │       │   └── version.test.ts
│   │       ├── array.ts
│   │       ├── async.ts
│   │       ├── browser.ts
│   │       ├── concurrency.ts
│   │       ├── extensionContext.ts
│   │       ├── gemini.ts
│   │       ├── hash.ts
│   │       ├── rtl.ts
│   │       ├── safariStorage.ts
│   │       ├── selectors.ts
│   │       ├── storageMigration.ts
│   │       ├── text.ts
│   │       ├── updateReminder.ts
│   │       └── version.ts
│   ├── features/
│   │   ├── backup/
│   │   │   ├── index.ts
│   │   │   ├── services/
│   │   │   │   ├── BackupService.ts
│   │   │   │   └── PromptImportExportService.ts
│   │   │   └── types/
│   │   │       └── backup.ts
│   │   ├── contextSync/
│   │   │   ├── adapters/
│   │   │   │   └── index.ts
│   │   │   ├── services/
│   │   │   │   └── SyncService.ts
│   │   │   └── types.ts
│   │   ├── export/
│   │   │   ├── services/
│   │   │   │   ├── ConversationExportService.ts
│   │   │   │   ├── DOMContentExtractor.ts
│   │   │   │   ├── DeepResearchPDFPrintService.ts
│   │   │   │   ├── ImageExportService.ts
│   │   │   │   ├── ImageRenderService.ts
│   │   │   │   ├── MarkdownFormatter.ts
│   │   │   │   ├── PDFPrintService.ts
│   │   │   │   └── __tests__/
│   │   │   │       ├── ConversationExportService.test.ts
│   │   │   │       ├── DOMContentExtractor.test.ts
│   │   │   │       ├── DeepResearchPDFPrintService.test.ts
│   │   │   │       ├── ImageExportService.test.ts
│   │   │   │       ├── ImageRenderService.test.ts
│   │   │   │       ├── MarkdownFormatter.test.ts
│   │   │   │       ├── PDFPrintService.safari.test.ts
│   │   │   │       └── PDFPrintService.test.ts
│   │   │   ├── types/
│   │   │   │   ├── errors.ts
│   │   │   │   └── export.ts
│   │   │   └── ui/
│   │   │       ├── ExportDialog.ts
│   │   │       ├── ExportErrorMessage.ts
│   │   │       ├── ExportToast.ts
│   │   │       └── __tests__/
│   │   │           ├── ExportDialog.safariHint.test.ts
│   │   │           ├── ExportDialog.test.ts
│   │   │           ├── ExportErrorMessage.test.ts
│   │   │           └── ExportToast.test.ts
│   │   ├── folder/
│   │   │   ├── services/
│   │   │   │   └── FolderImportExportService.ts
│   │   │   └── types/
│   │   │       └── import-export.ts
│   │   └── formulaCopy/
│   │       ├── FormulaCopyService.test.ts
│   │       ├── FormulaCopyService.ts
│   │       └── index.ts
│   ├── global.d.ts
│   ├── hooks/
│   │   ├── useDarkMode.ts
│   │   └── useWidthAdjuster.ts
│   ├── lib/
│   │   └── utils.ts
│   ├── locales/
│   │   ├── ar/
│   │   │   └── messages.json
│   │   ├── en/
│   │   │   └── messages.json
│   │   ├── es/
│   │   │   └── messages.json
│   │   ├── fr/
│   │   │   └── messages.json
│   │   ├── ja/
│   │   │   └── messages.json
│   │   ├── ko/
│   │   │   └── messages.json
│   │   ├── pt/
│   │   │   └── messages.json
│   │   ├── ru/
│   │   │   └── messages.json
│   │   ├── zh/
│   │   │   └── messages.json
│   │   └── zh_TW/
│   │       └── messages.json
│   ├── pages/
│   │   ├── background/
│   │   │   └── index.ts
│   │   ├── content/
│   │   │   ├── changelog/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── changelog.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── notes/
│   │   │   │       ├── 1.2.9.md
│   │   │   │       ├── 1.3.0.md
│   │   │   │       ├── 1.3.1.md
│   │   │   │       ├── 1.3.2.md
│   │   │   │       ├── 1.3.3.md
│   │   │   │       ├── 1.3.4.md
│   │   │   │       ├── 1.3.5.md
│   │   │   │       └── 1.3.6.md
│   │   │   ├── chatWidth/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── chatWidth.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── contextSync/
│   │   │   │   ├── capture.ts
│   │   │   │   └── index.ts
│   │   │   ├── deepResearch/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── menuButton.test.ts
│   │   │   │   │   └── reportExtractor.test.ts
│   │   │   │   ├── download.ts
│   │   │   │   ├── extractor.ts
│   │   │   │   ├── formatter.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── menuButton.ts
│   │   │   │   ├── reportExtractor.ts
│   │   │   │   └── types.ts
│   │   │   ├── defaultModel/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── modelLocker.test.ts
│   │   │   │   ├── modelLocker.ts
│   │   │   │   └── styles.css
│   │   │   ├── editInputWidth/
│   │   │   │   └── index.ts
│   │   │   ├── export/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── conversationDom.test.ts
│   │   │   │   │   ├── conversationMenuI18n.test.ts
│   │   │   │   │   ├── conversationMenuInjection.test.ts
│   │   │   │   │   ├── responseActionImageButton.test.ts
│   │   │   │   │   ├── responseImageCopy.test.ts
│   │   │   │   │   ├── selectionModeInteraction.test.ts
│   │   │   │   │   ├── selectionUtils.test.ts
│   │   │   │   │   ├── sidebarConversationTarget.test.ts
│   │   │   │   │   ├── sidebarExportResume.test.ts
│   │   │   │   │   └── topNodePreload.test.ts
│   │   │   │   ├── conversationDom.ts
│   │   │   │   ├── conversationMenuInjection.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── responseActionImageButton.ts
│   │   │   │   ├── responseImageCopy.ts
│   │   │   │   ├── selectionUtils.ts
│   │   │   │   ├── sidebarConversationTarget.ts
│   │   │   │   ├── sidebarExportResume.ts
│   │   │   │   └── topNodePreload.ts
│   │   │   ├── folder/
│   │   │   │   ├── README.md
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── aistudio.test.ts
│   │   │   │   │   ├── conversationSort.test.ts
│   │   │   │   │   ├── folderNameInteraction.test.ts
│   │   │   │   │   ├── moveToFolderMenuItem.test.ts
│   │   │   │   │   └── treeIndent.test.ts
│   │   │   │   ├── aistudio.ts
│   │   │   │   ├── conversationSort.ts
│   │   │   │   ├── folderColors.ts
│   │   │   │   ├── gemConfig.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── manager.ts
│   │   │   │   ├── moveToFolderMenuItem.ts
│   │   │   │   ├── storage/
│   │   │   │   │   └── FolderStorageAdapter.ts
│   │   │   │   └── types.ts
│   │   │   ├── folderSpacing/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── folderSpacing.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── fork/
│   │   │   │   ├── ForkNodesService.ts
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── ForkNodesService.test.ts
│   │   │   │   │   ├── branching.test.ts
│   │   │   │   │   ├── chatPairs.test.ts
│   │   │   │   │   ├── featureFlag.test.ts
│   │   │   │   │   ├── forkContext.test.ts
│   │   │   │   │   ├── index.test.ts
│   │   │   │   │   ├── markdown.test.ts
│   │   │   │   │   └── turnId.test.ts
│   │   │   │   ├── branching.ts
│   │   │   │   ├── chatPairs.ts
│   │   │   │   ├── featureFlag.ts
│   │   │   │   ├── forkContext.ts
│   │   │   │   ├── forkTypes.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── markdown.ts
│   │   │   │   └── turnId.ts
│   │   │   ├── gemsHider/
│   │   │   │   └── index.ts
│   │   │   ├── index.tsx
│   │   │   ├── inputCollapse/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── inputCollapse.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── katexConfig/
│   │   │   │   └── index.ts
│   │   │   ├── markdownPatcher/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── fixBrokenBoldTags.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── mermaid/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── mermaid.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── preventAutoScroll/
│   │   │   │   └── index.ts
│   │   │   ├── prompt/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── scrollHint.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── scrollHint.ts
│   │   │   ├── quoteReply/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── quoteReply.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── recentsHider/
│   │   │   │   └── index.ts
│   │   │   ├── sendBehavior/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── sendBehavior.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── utils.ts
│   │   │   ├── shared/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── nativeMenuItemTemplate.test.ts
│   │   │   │   └── nativeMenuItemTemplate.ts
│   │   │   ├── sidebarAutoHide/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── SidebarAutoHide.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── sidebarWidth/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── sidebarWidthCentering.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── timeline/
│   │   │   │   ├── EventBus.ts
│   │   │   │   ├── StarredMessagesService.ts
│   │   │   │   ├── TimelinePreviewPanel.ts
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── TimelineBootstrap.test.ts
│   │   │   │   │   ├── TimelineManagerActiveIndex.test.ts
│   │   │   │   │   ├── TimelineManagerFlowClickActiveReset.test.ts
│   │   │   │   │   ├── TimelineManagerNavigationRefresh.test.ts
│   │   │   │   │   ├── TimelineManagerPreviewPanelReposition.test.ts
│   │   │   │   │   ├── TimelineManagerSelectorPriority.test.ts
│   │   │   │   │   ├── TimelineManagerSummaryExtraction.test.ts
│   │   │   │   │   ├── TimelineManagerTooltipDirection.test.ts
│   │   │   │   │   └── TimelinePreviewPanel.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── manager.ts
│   │   │   │   ├── starredTypes.ts
│   │   │   │   └── types.ts
│   │   │   ├── timestamp/
│   │   │   │   ├── TimestampService.ts
│   │   │   │   └── __tests__/
│   │   │   │       └── TimestampService.test.ts
│   │   │   ├── titleUpdater/
│   │   │   │   └── index.ts
│   │   │   ├── visualEffects/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── rain.test.ts
│   │   │   │   │   ├── sakura.test.ts
│   │   │   │   │   └── snow.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── rain.ts
│   │   │   │   ├── sakura.ts
│   │   │   │   └── snow.ts
│   │   │   └── watermarkRemover/
│   │   │       ├── __tests__/
│   │   │       │   ├── downloadButton.test.ts
│   │   │       │   ├── downloadToasts.test.ts
│   │   │       │   ├── fetchInterceptor.test.ts
│   │   │       │   └── statusToast.test.ts
│   │   │       ├── alphaMap.ts
│   │   │       ├── blendModes.ts
│   │   │       ├── credits.ts
│   │   │       ├── downloadButton.ts
│   │   │       ├── index.ts
│   │   │       ├── statusToast.ts
│   │   │       └── watermarkEngine.ts
│   │   ├── devtools/
│   │   │   ├── index.html
│   │   │   └── index.ts
│   │   ├── options/
│   │   │   ├── Options.css
│   │   │   ├── Options.tsx
│   │   │   ├── index.css
│   │   │   ├── index.html
│   │   │   └── index.tsx
│   │   ├── panel/
│   │   │   ├── Panel.css
│   │   │   ├── Panel.tsx
│   │   │   ├── index.css
│   │   │   ├── index.html
│   │   │   └── index.tsx
│   │   └── popup/
│   │       ├── Popup.tsx
│   │       ├── __tests__/
│   │       │   └── latestVersion.test.ts
│   │       ├── components/
│   │       │   ├── CloudSyncSettings.tsx
│   │       │   ├── ContextSyncSettings.tsx
│   │       │   ├── KeyboardShortcutSettings.tsx
│   │       │   ├── StarredHistory.tsx
│   │       │   ├── WebsiteLogos.tsx
│   │       │   ├── WidthSlider.tsx
│   │       │   └── __tests__/
│   │       │       └── CloudSyncSettings.test.tsx
│   │       ├── index.css
│   │       ├── index.html
│   │       ├── index.tsx
│   │       └── utils/
│   │           └── latestVersion.ts
│   ├── tests/
│   │   └── setup.ts
│   ├── utils/
│   │   ├── __tests__/
│   │   │   ├── i18n.test.ts
│   │   │   ├── language.test.ts
│   │   │   ├── mergeForkNodes.test.ts
│   │   │   ├── mergeStarredMessages.test.ts
│   │   │   └── translations.test.ts
│   │   ├── i18n.ts
│   │   ├── language.ts
│   │   ├── localeMessages.ts
│   │   ├── merge.test.ts
│   │   ├── merge.ts
│   │   └── translations.ts
│   └── vite-env.d.ts
├── tsconfig.json
├── vite.config.base.ts
├── vite.config.chrome.ts
├── vite.config.firefox.ts
├── vite.config.safari.ts
└── vitest.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .claude/rules/content-scripts.md
================================================
---
globs: ["src/pages/content/**", "public/contentStyle.css"]
---

# Content Script Rules

## CSS
- All injected CSS classes MUST be prefixed with `gv-` (e.g., `.gv-rtl`, `.gv-pm-trigger`)
- Styles go in `public/contentStyle.css`
- Support both light and dark themes: use `.theme-host.light-theme` / `.theme-host.dark-theme` overrides, NOT `@media (prefers-color-scheme)`
- RTL layout: use `body.gv-rtl` selector for RTL overrides (see `src/core/utils/rtl.ts`)

## Storage
- Content scripts use `chrome.storage` / `browser.storage` directly via ExtGlobal — this is the exception to the "use StorageService" rule
- StorageService is for popup/background/options contexts

## DOM Injection
- Each content script sub-module in `src/pages/content/` is self-contained
- Bridge between Gemini UI and Extension — Gemini DOM structure may change without notice
- Safari has limitations: cloud sync, watermark removal, image export are disabled. Check `isSafari()` guards.
- Extension context can be invalidated after update/reload. Use `isExtensionContextInvalidatedError()`.

## Material Symbols Icons
- When adding new icons in popup, add the icon name to `icon_names=` in the Google Fonts URL in `src/pages/popup/index.html`


================================================
FILE: .claude/rules/high-complexity.md
================================================
---
globs: ["src/core/services/StorageService.ts", "src/core/services/DataBackupService.ts", "src/core/services/GoogleDriveSyncService.ts", "src/core/services/AccountIsolationService.ts", "src/features/folder/**", "src/features/export/**"]
---

# High-Complexity Modules — Edit with Caution

| Module | Risk | Notes |
|--------|------|-------|
| `StorageService` | Sync/local/session logic + migration. Single source of truth for persistence. | Do not modify lightly. |
| `DataBackupService` | Multi-layer backup. Race conditions during unload. | Critical for data safety. |
| `GoogleDriveSyncService` | OAuth2 cloud sync (folders, prompts, starred). | Requires OAuth2 identity. |
| `AccountIsolationService` | Hard account isolation for multi-account. | Integrates with Drive sync. |
| `features/folder` | Drag-and-drop + cloud sync UI. DOM manipulation + state sync. | Watch for infinite loops. |
| `features/export` | JSON/MD/PDF/Image export + Deep Research. | Fragile to Gemini UI changes. Multi-browser compat. |

## Before modifying these modules
1. Read the entire file first — not just the section you plan to change
2. List all existing features that might be affected
3. Ensure zero destructiveness to user data
4. Run full test suite after changes


================================================
FILE: .claude/rules/i18n.md
================================================
---
globs: ["src/locales/**"]
---

# i18n / Translation Rules

## 10 Locales (ALL must be updated together)
`en`, `ar`, `es`, `fr`, `ja`, `ko`, `pt`, `ru`, `zh`, `zh_TW`

## When adding a new translation key
1. Add the key to ALL 10 `src/locales/*/messages.json` files
2. English (`en`) is the source — write it first, then translate to all others
3. Keys are flat strings in JSON, no nesting

## When removing a translation key
- Remove from ALL 10 locale files

## Quality
- Translations should be natural, not machine-literal
- Arabic (`ar`) is RTL — ensure UI handles it (see `src/core/utils/rtl.ts`)


================================================
FILE: .claude/rules/typescript.md
================================================
---
globs: ["src/**/*.ts", "src/**/*.tsx"]
---

# TypeScript Coding Standards

## DOs
- Prefer plain objects with interfaces/types for data structures
- Use `map`, `filter`, `reduce` for immutability
- Use `private`/`protected` in classes
- Use `unknown` + narrowing (Zod or custom guards) for type safety
- Use named exports: `export function X`
- Functional React: hooks at top level, strictly functional components

## DON'Ts
- **No `any` type.** Use `unknown` if you must, then narrow it.
- **No global variables** outside defined Services.
- **No `chrome.storage` in UI components** (`src/components/`, `src/pages/popup/`). Use `StorageService`.
- **No God Components.** Business logic belongs in `features/*/services/` or custom hooks, not UI files.
- **No magic strings.** Use constants or enums (StorageKeys, CSS classes).
- **No `console.log` in production.** Use `LoggerService` for critical info.

## Testing (Vitest + jsdom)
- Chrome `chrome` object is globally mocked in `src/tests/setup.ts`
- Mock specific storage: `(chrome.storage.sync.get as any).mockResolvedValue({ key: 'val' })`
- Run: `bun run test`, `bun run test <filename>`, `bun run test:coverage`


================================================
FILE: .claude/settings.json
================================================
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Task",
        "hooks": [
          {
            "type": "command",
            "command": "entire hooks claude-code post-task"
          }
        ]
      },
      {
        "matcher": "TodoWrite",
        "hooks": [
          {
            "type": "command",
            "command": "entire hooks claude-code post-todo"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Task",
        "hooks": [
          {
            "type": "command",
            "command": "entire hooks claude-code pre-task"
          }
        ]
      }
    ],
    "SessionEnd": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "entire hooks claude-code session-end"
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "entire hooks claude-code session-start"
          }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "entire hooks claude-code stop"
          }
        ]
      }
    ],
    "UserPromptSubmit": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "entire hooks claude-code user-prompt-submit"
          }
        ]
      }
    ]
  },
  "permissions": {
    "deny": [
      "Read(./.entire/metadata/**)"
    ]
  }
}


================================================
FILE: .claude/skills/safari-release/SKILL.md
================================================
---
name: safari-release
description: Build Safari extension with update check enabled, guide user through Xcode export, and create DMG for distribution. Use when user wants to release a new Safari version or create a Safari DMG.
user-invocable: true
---

# Safari Release Workflow

Build the Safari extension for manual distribution and create a signed DMG.

## Steps

### 1. Read version from package.json

Read `package.json` to get the current version number. Store it as `VERSION` for later steps.

### 2. Build Safari with update check enabled

Run the following command:

```bash
ENABLE_SAFARI_UPDATE_CHECK=true bun run build:safari
```

Wait for the build to complete successfully. If it fails, report the error and stop.

### 3. Prompt user for Xcode export

Tell the user:

> Safari build complete (`dist_safari/`). Please complete the following steps in Xcode:
>
> 1. Open the Xcode project (if not already open)
> 2. **Product → Archive**
> 3. **Window → Organizer** → select the archive → **Distribute App**
> 4. Export the signed `Gemini Voyager.app` into `safari/Models/dmg_source/`
>
> Let me know when you're done exporting.

**Wait for the user to confirm** before proceeding. Do NOT continue until the user says they're done.

### 4. Verify the exported app exists

Check that `safari/Models/dmg_source/Gemini Voyager.app` exists:

```bash
ls "safari/Models/dmg_source/Gemini Voyager.app"
```

If it doesn't exist, ask the user to check their export path.

### 5. Create DMG

Run `create-dmg` in the `safari/Models` directory:

```bash
cd safari/Models && create-dmg \
  --volname "Gemini Voyager" \
  --window-size 600 400 \
  --icon-size 100 \
  --icon "Gemini Voyager.app" 175 190 \
  --app-drop-link 425 190 \
  "voyager-v${VERSION}.dmg" \
  dmg_source
```

### 6. Verify and report

Confirm the DMG was created at `safari/Models/voyager-v${VERSION}.dmg` and report success to the user.


================================================
FILE: .editorconfig
================================================
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false


================================================
FILE: .entire/.gitignore
================================================
tmp/
settings.local.json
metadata/
logs/


================================================
FILE: .entire/settings.json
================================================
{
  "strategy": "manual-commit",
  "enabled": true,
  "telemetry": false
}


================================================
FILE: .gitattributes
================================================
# Auto detect text files and normalize line endings to LF
* text=auto eol=lf

# Explicitly declare file types
*.ts text eol=lf
*.tsx text eol=lf
*.js text eol=lf
*.jsx text eol=lf
*.json text eol=lf
*.md text eol=lf
*.css text eol=lf
*.html text eol=lf
*.yml text eol=lf
*.yaml text eol=lf

# Binary files (don't modify)
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.woff binary
*.woff2 binary
*.ttf binary
*.eot binary
bun.lockb binary


================================================
FILE: .github/CONTRIBUTING.md
================================================
> **🌐 语言 / Language**: [中文](#贡献指南) | [English](#contributing-to-gemini-voyager) | [Español](CONTRIBUTING_ES.md) | [Français](CONTRIBUTING_FR.md) | [日本語](CONTRIBUTING_JA.md)

---

# 贡献指南

> [!CAUTION]
> **本项目暂时不接受任何新功能的 PR。** 如果你有一个很想做的功能,请按以下流程操作:
>
> 1. **先开一个 Issue 与维护者讨论**你的想法和方案
> 2. **等待维护者同意,并确定了一个好的实现方案后**,再开始编码并提交 PR
>
> 未经讨论直接提交的新功能 PR 将被直接关闭,不予审核。感谢理解。

> [!IMPORTANT]
> **项目状态:低频维护。** 回复较慢。优先处理带测试的 PR。

感谢你考虑为 Voyager 做出贡献!🚀

本文档提供贡献的指南和说明。我们欢迎错误修复、文档改进和翻译等贡献。关于新功能,请务必先通过 Issue 进行讨论。

## 🚫 AI 政策

**本项目拒绝接受任何未经人工复核的 AI 生成的 PR。**

虽然 AI 是很好的辅助工具,但“懒惰”的复制粘贴贡献会浪费维护者的时间。

- **缺乏逻辑解释** 或缺少必要测试的 PR 将将被拒绝。
- 你必须理解并对你提交的每一行代码负责。
- **Git 协作能力**:你应熟悉 GitHub 和 Git 的基本工作流,确保能在 AI Agent 的辅助下正确进行开源协作。如果你对此尚不熟悉,建议先学习相关知识,请保持 PR 中的 Git 历史整洁,避免出现混乱的提交记录。

## 目录

- [快速开始](#快速开始)
- [认领 Issue](#认领-issue)
- [开发环境设置](#开发环境设置)
- [进行更改](#进行更改)
- [提交 Pull Request](#提交-pull-request)
- [代码风格](#代码风格)
- [添加 Gem 支持](#添加-gem-支持)
- [许可证](#许可证)

---

## 快速开始

### 前置要求

- **Bun** 1.0+(必需)
- 用于测试的 Chromium 内核浏览器(Chrome、Edge、Brave 等)
- **Firefox:必须进行测试。**
- **Safari:作为可选项目**。如果有环境请进行测试;或者由 AI/自行判断该功能是否为 Safari 不支持的功能,并请予以标注。

### 快速启动

```bash
# 克隆仓库
git clone https://github.com/Nagi-ovo/gemini-voyager.git
cd gemini-voyager

# 安装依赖
bun install

# 启动开发模式
bun run dev
```

---

## 认领 Issue

为避免重复工作并协调贡献:

### 1. 检查现有工作

在开始之前,检查 issue 的 **Assignees** 部分,确认是否已有人被分配。

### 2. 认领 Issue

在任何未分配的 issue 上评论 `/claim`,机器人将自动将你分配为负责人。

### 3. 取消认领

如果你无法继续处理某个 issue,评论 `/unclaim` 即可释放它供他人处理。

### 4. 贡献意愿复选框

创建 issue 时,你可以勾选"我愿意贡献代码"复选框,表明你有兴趣实现该功能或修复。

---

## 开发环境设置

### 安装依赖

```bash
bun install
```

### 可用命令

| 命令                  | 描述                             |
| --------------------- | -------------------------------- |
| `bun run dev`         | 启动 Chrome 开发模式(热重载)   |
| `bun run dev:firefox` | 启动 Firefox 开发模式            |
| `bun run dev:safari`  | 启动 Safari 开发模式(仅 macOS) |
| `bun run build`       | Chrome 生产构建                  |
| `bun run build:all`   | 所有浏览器生产构建               |
| `bun run lint`        | 运行 ESLint 并自动修复           |
| `bun run typecheck`   | 运行 TypeScript 类型检查         |
| `bun run test`        | 运行测试套件                     |

### 加载扩展

1. 运行 `bun run dev` 启动开发构建
2. 打开 Chrome,访问 `chrome://extensions/`
3. 启用"开发者模式"
4. 点击"加载已解压的扩展程序",选择 `dist_chrome` 文件夹

---

## 进行更改

### 开始之前

1. **从 `main` 创建分支**:

   ```bash
   git checkout -b feature/your-feature-name
   # 或
   git checkout -b fix/your-bug-fix
   ```

2. **关联 Issue** - 在实现一个新功能时,请**务必先开启一个 Issue 进行讨论**。未经讨论直接提交的新功能 PR 将被关闭。在提交 PR 时,请链接该 Issue。

### 提交前检查清单

提交前,请务必运行:

```bash
bun run lint       # 修复代码风格问题
bun run format     # 格式化代码
bun run typecheck  # 检查类型
bun run build      # 验证构建成功
bun run test       # 运行测试
```

并确保:

1. 你的更改实现了预期功能。
2. 你的更改没有影响现有的原有功能。

---

## 测试策略

我们遵循“基于 ROI”的测试策略:**测逻辑,不测 DOM。**

1. **必须测 (Logic)**:核心服务(Storage, Backup)、数据解析和工具函数。必须使用 TDD。
2. **建议测 (State)**:复杂的 UI 状态(如文件夹 Reducer)。
3. **跳过 (Fragile)**:直接 DOM 操作(Content Scripts)和纯 UI 组件。请使用防御性编程代替。

---

## 提交 Pull Request

### PR 指南

1. **标题**:使用清晰的描述性标题(如 "feat: add dark mode toggle" 或 "fix: timeline scroll sync")
2. **描述**:解释你做了什么更改以及原因
3. **用户影响**:描述用户将如何受到影响
4. **可视化证据(严格)**:对于任何 UI 修改或新功能,**必须**提供截图或屏幕录制。**没有截图 = 不予审核/回复。**
5. **Issue 引用**:链接相关 issue(如 "Closes #123")
6. **测试与逻辑**:PR 必须包含单元测试并清晰解释修改逻辑。不接受没有上下文的“魔法”修复。

### 提交信息格式

遵循 [Conventional Commits](https://www.conventionalcommits.org/):

- `feat:` - 新功能
- `fix:` - 错误修复
- `docs:` - 文档更改
- `chore:` - 维护任务
- `refactor:` - 代码重构
- `test:` - 添加或更新测试

---

## 代码风格

### 通用指南

- **优先使用提前返回**而非嵌套条件
- **使用描述性名称** - 避免缩写
- **避免魔法数字** - 使用命名常量
- **匹配现有风格** - 一致性优于偏好

### TypeScript 约定

- **PascalCase**:类、接口、类型、枚举、React 组件
- **camelCase**:函数、变量、方法
- **UPPER_SNAKE_CASE**:常量

### 导入顺序

1. React 及相关导入
2. 第三方库
3. 内部绝对导入(`@/...`)
4. 相对导入(`./...`)
5. 仅类型导入

---

## 添加 Gem 支持

如需为新 Gem(官方 Google Gems 或自定义 Gems)添加支持:

1. 打开 `src/pages/content/folder/gemConfig.ts`
2. 在 `GEM_CONFIG` 数组中添加新条目:

```typescript
{
  id: 'your-gem-id',           // URL 中的 ID:/gem/your-gem-id/...
  name: 'Your Gem Name',       // 显示名称
  icon: 'material_icon_name',  // Google Material Symbols 图标
}
```

### 查找 Gem ID

- 打开与该 Gem 的对话
- 检查 URL:`https://gemini.google.com/app/gem/[GEM_ID]/...`
- 在配置中使用 `[GEM_ID]` 部分

### 选择图标

使用有效的 [Google Material Symbols](https://fonts.google.com/icons) 图标名称:

| 图标           | 用途           |
| -------------- | -------------- |
| `auto_stories` | 学习、教育     |
| `lightbulb`    | 创意、头脑风暴 |
| `work`         | 职业、专业     |
| `code`         | 编程、技术     |
| `analytics`    | 数据、分析     |

---

## 项目范围

Voyager 通过以下功能增强 Gemini AI 聊天体验:

- 时间线导航
- 文件夹组织
- 指令宝库
- 聊天导出
- UI 自定义

**不在范围内**:网站爬取、网络拦截、账户自动化。

---

## 获取帮助

- 💬 [GitHub Discussions](https://github.com/Nagi-ovo/gemini-voyager/discussions) - 提问
- 🐛 [Issues](https://github.com/Nagi-ovo/gemini-voyager/issues) - 报告错误
- 📖 [文档](https://gemini-voyager.vercel.app/) - 阅读文档

---

## 许可证

提交贡献即表示你同意你的贡献将采用 [GPLv3 许可证](../LICENSE)。

---

# Contributing to Voyager

> [!CAUTION]
> **This project is currently NOT accepting PRs for new features.** If you have a feature you'd really like to build, please follow this process:
>
> 1. **Open an Issue first** to discuss your idea and approach with the maintainer
> 2. **Wait for approval and a solid implementation plan** before writing any code or submitting a PR
>
> New feature PRs submitted without prior discussion will be closed without review. Thank you for understanding.

> [!IMPORTANT]
> **Project Status: Low Maintenance.** Expect delays in response. PRs with tests are prioritized.

Thank you for considering contributing to Voyager! 🚀

This document provides guidelines and instructions for contributing. We welcome bug fixes, documentation improvements, and translations. For new features, please discuss via an Issue first.

## 🚫 AI Policy

**We explicitly reject AI-generated PRs that have not been manually verified.**

While AI tools are great assistants, "lazy" copy-paste contributions waste maintainer time.

- **Low-quality AI PRs** will be closed immediately without discussion.
- **PRs without explanation** of the logic or missing necessary tests will be rejected.
- You must understand and take responsibility for every line of code you submit.
- **Workflow Proficiency**: You should be familiar with GitHub and Git workflows and able to collaborate correctly using AI tools. If you are new to this, please learn the basics first to ensure a clean Git history in your PRs.

## Table of Contents

- [Getting Started](#getting-started)
- [Claiming an Issue](#claiming-an-issue)
- [Development Setup](#development-setup)
- [Making Changes](#making-changes)
- [Submitting a Pull Request](#submitting-a-pull-request)
- [Code Style](#code-style)
- [Adding Gem Support](#adding-gem-support)
- [License](#license)

---

## Getting Started

### Prerequisites

- **Bun** 1.0+ (Required)
- A Chromium-based browser for testing (Chrome, Edge, Brave, etc.)
- **Firefox: Mandatory testing.**
- **Safari: Optional.** If you have the environment, please test it. Alternatively, let AI or use your own judgment to determine if the feature is unsupported on Safari and label it accordingly.

### Quick Start

```bash
# Clone the repository
git clone https://github.com/Nagi-ovo/gemini-voyager.git
cd gemini-voyager

# Install dependencies
bun install

# Start development mode
bun run dev
```

---

## Claiming an Issue

To avoid duplicate work and coordinate contributions:

### 1. Check for Existing Work

Before starting, check if the issue is already assigned to someone by looking at the **Assignees** section.

### 2. Claim an Issue

Comment `/claim` on any unassigned issue to automatically assign yourself. A bot will confirm the assignment.

### 3. Unclaim if Needed

If you can no longer work on an issue, comment `/unclaim` to release it for others.

### 4. Contribution Checkbox

When creating issues, you can check the "I am willing to contribute code" checkbox to indicate your interest in implementing the feature or fix.

---

## Development Setup

### Install Dependencies

```bash
bun install
```

### Available Commands

| Command               | Description                                   |
| --------------------- | --------------------------------------------- |
| `bun run dev`         | Start Chrome development mode with hot reload |
| `bun run dev:firefox` | Start Firefox development mode                |
| `bun run dev:safari`  | Start Safari development mode (macOS only)    |
| `bun run build`       | Production build for Chrome                   |
| `bun run build:all`   | Production build for all browsers             |
| `bun run lint`        | Run ESLint with auto-fix                      |
| `bun run typecheck`   | Run TypeScript type checking                  |
| `bun run test`        | Run test suite                                |

### Loading the Extension

1. Run `bun run dev` to start the development build
2. Open Chrome and go to `chrome://extensions/`
3. Enable "Developer mode"
4. Click "Load unpacked" and select the `dist_chrome` folder

---

## Making Changes

### Before You Start

1. **Create a branch** from `main`:

   ```bash
   git checkout -b feature/your-feature-name
   # or
   git checkout -b fix/your-bug-fix
   ```

2. **Link Issues** - When implementing a new feature, you **must first open an Issue for discussion**. PRs for new features submitted without prior discussion will be closed. When submitting a PR, please link that Issue.

### Pre-Commit Checklist

Before submitting, always run:

```bash
bun run lint       # Fix linting issues
bun run format     # Format code
bun run typecheck  # Check types
bun run build      # Verify build succeeds
bun run test       # Run tests
```

Ensure that:

1. Your changes achieve the desired functionality.
2. Your changes do not negatively affect existing features.

---

## Testing Strategy

We follow a "ROI-based" testing strategy: **Test Logic, Not DOM.**

1. **Must Have (Logic)**: Core services (Storage, Backup), Data parsers, and Utils. TDD is required here.
2. **Should Have (State)**: Complex UI state (e.g., Folder reducer).
3. **Skip (Fragile)**: Direct DOM manipulation (Content Scripts) and pure UI components. Use defensive programming instead.

---

## Submitting a Pull Request

### PR Guidelines

1. **Title**: Use a clear, descriptive title (e.g., "feat: add dark mode toggle" or "fix: timeline scroll sync")
2. **Description**: Explain what changes you made and why
3. **User Impact**: Describe how users will be affected
4. **Visual Proof (Strict)**: For ANY UI changes or new features, you **MUST** provide screenshots or screen recordings. **No screenshot = No review/reply.**
5. **Issue Reference**: Link related issues (e.g., "Closes #123")
6. **Tests & Logic**: PRs must include unit tests and a clear explanation of the logic. "Magic" fixes without context are not accepted.

### Commit Message Format

Follow [Conventional Commits](https://www.conventionalcommits.org/):

- `feat:` - New features
- `fix:` - Bug fixes
- `docs:` - Documentation changes
- `chore:` - Maintenance tasks
- `refactor:` - Code refactoring
- `test:` - Adding or updating tests

---

## Code Style

### General Guidelines

- **Prefer early returns** over nested conditionals
- **Use descriptive names** - avoid abbreviations
- **Avoid magic numbers** - use named constants
- **Match existing style** - consistency over preference

### TypeScript Conventions

- **PascalCase**: Classes, interfaces, types, enums, React components
- **camelCase**: Functions, variables, methods
- **UPPER_SNAKE_CASE**: Constants

### Import Order

1. React & React-related imports
2. Third-party libraries
3. Internal absolute imports (`@/...`)
4. Relative imports (`./...`)
5. Type-only imports

```typescript
import React, { useState } from 'react';

import { marked } from 'marked';

import { Button } from '@/components/ui/Button';
import { StorageService } from '@/core/services/StorageService';
import type { FolderData } from '@/core/types/folder';

import { parseData } from './parser';
```

---

## Adding Gem Support

To add support for a new Gem (official Google Gems or custom Gems):

1. Open `src/pages/content/folder/gemConfig.ts`
2. Add a new entry to the `GEM_CONFIG` array:

```typescript
{
  id: 'your-gem-id',           // From URL: /gem/your-gem-id/...
  name: 'Your Gem Name',       // Display name
  icon: 'material_icon_name',  // Google Material Symbols icon
}
```

### Finding the Gem ID

- Open a conversation with the Gem
- Check the URL: `https://gemini.google.com/app/gem/[GEM_ID]/...`
- Use the `[GEM_ID]` portion in your configuration

### Choosing an Icon

Use valid [Google Material Symbols](https://fonts.google.com/icons) icon names:

| Icon           | Use Case               |
| -------------- | ---------------------- |
| `auto_stories` | Learning, Education    |
| `lightbulb`    | Ideas, Brainstorming   |
| `work`         | Career, Professional   |
| `code`         | Programming, Technical |
| `analytics`    | Data, Analysis         |

---

## Project Scope

Voyager enhances the Gemini AI chat experience with:

- Timeline navigation
- Folder organization
- Prompt vault
- Chat export
- UI customization

**Out of scope**: Site scraping, network interception, account automation.

---

## Getting Help

- 💬 [GitHub Discussions](https://github.com/Nagi-ovo/gemini-voyager/discussions) - Ask questions
- 🐛 [Issues](https://github.com/Nagi-ovo/gemini-voyager/issues) - Report bugs
- 📖 [Documentation](https://gemini-voyager.vercel.app/) - Read the docs

---

## License

By contributing, you agree that your contributions will be licensed under the [GPLv3 License](../LICENSE).


================================================
FILE: .github/CONTRIBUTING_ES.md
================================================
# Guía de Contribución

> [!CAUTION]
> **Este proyecto actualmente NO acepta PRs para nuevas funcionalidades.** Si tienes una funcionalidad que realmente te gustaría desarrollar, sigue este proceso:
>
> 1. **Abre un Issue primero** para discutir tu idea y enfoque con el mantenedor
> 2. **Espera la aprobación y un plan de implementación sólido** antes de escribir código o enviar un PR
>
> Los PRs de nuevas funcionalidades enviados sin discusión previa serán cerrados sin revisión. Gracias por tu comprensión.

> [!IMPORTANT]
> **Estado del proyecto: Mantenimiento bajo.** Espere retrasos en las respuestas. Se priorizan los PR con pruebas.

¡Gracias por considerar contribuir a Voyager! 🚀

Este documento proporciona pautas e instrucciones para contribuir. Damos la bienvenida a correcciones de errores, mejoras en la documentación y traducciones. Para nuevas funcionalidades, por favor discútelo primero mediante un Issue.

## 🚫 Política de IA

**Rechazamos explícitamente los PR generados por IA que no hayan sido verificados manualmente.**

Aunque las herramientas de IA son grandes asistentes, las contribuciones de "copiar y pegar" sin revisión hacen perder tiempo a los mantenedores.

- **Los PR de IA de baja calidad** se cerrarán inmediatamente sin discusión.
- **Los PR sin explicación** de la lógica o que carezcan de las pruebas necesarias serán rechazados.
- Debes entender y asumir la responsabilidad de cada línea de código que envíes.

## Tabla de Contenidos

- [Comenzando](#comenzando)
- [Reclamar un Problema](#reclamar-un-problema)
- [Configuración de Desarrollo](#configuración-de-desarrollo)
- [Realizando Cambios](#realizando-cambios)
- [Enviar un Pull Request](#enviar-un-pull-request)
- [Estilo de Código](#estilo-de-código)
- [Agregar Soporte para Gem](#agregar-soporte-para-gem)
- [Licencia](#licencia)

---

## Comenzando

### Requisitos Previos

- **Bun** 1.0+ (Requerido)
- Un navegador basado en Chromium para pruebas (Chrome, Edge, Brave, etc.)

### Inicio Rápido

```bash
# Clonar el repositorio
git clone https://github.com/Nagi-ovo/gemini-voyager.git
cd gemini-voyager

# Instalar dependencias
bun install

# Iniciar modo de desarrollo
bun run dev
```

---

## Reclamar un Problema

Para evitar trabajo duplicado y coordinar contribuciones:

### 1. Verificar Trabajo Existente

Antes de comenzar, verifica si el problema ya está asignado a alguien mirando la sección **Assignees**.

### 2. Reclamar un Problema

Comenta `/claim` en cualquier problema no asignado para asignártelo automáticamente. Un bot confirmará la asignación.

### 3. Liberar si es Necesario

Si ya no puedes trabajar en un problema, comenta `/unclaim` para liberarlo para otros.

### 4. Casilla de Verificación de Contribución

Al crear problemas, puedes marcar la casilla "I am willing to contribute code" para indicar tu interés en implementar la funcionalidad o corrección.

---

## Configuración de Desarrollo

### Instalar Dependencias

```bash
bun install
```

### Comandos Disponibles

| Comando               | Descripción                                           |
| --------------------- | ----------------------------------------------------- |
| `bun run dev`         | Iniciar modo desarrollo Chrome con recarga automática |
| `bun run dev:firefox` | Iniciar modo desarrollo Firefox                       |
| `bun run dev:safari`  | Iniciar modo desarrollo Safari (solo macOS)           |
| `bun run build`       | Compilación de producción para Chrome                 |
| `bun run build:all`   | Compilación de producción para todos los navegadores  |
| `bun run lint`        | Ejecutar ESLint con corrección automática             |
| `bun run typecheck`   | Ejecutar comprobación de tipos TypeScript             |
| `bun run test`        | Ejecutar conjunto de pruebas                          |

### Cargar la Extensión

1. Ejecuta `bun run dev` para iniciar la compilación de desarrollo
2. Abre Chrome y ve a `chrome://extensions/`
3. Habilita el "Modo de desarrollador"
4. Haz clic en "Cargar descomprimida" y selecciona la carpeta `dist_chrome`

---

## Realizando Cambios

### Antes de Empezar

1. **Crea una rama** desde `main`:

   ```bash
   git checkout -b feature/nombre-de-tu-funcionalidad
   # o
   git checkout -b fix/tu-correccion-de-error
   ```

2. **Vincular Issues** - Al implementar una nueva funcionalidad, **primero debes abrir un Issue de discusión**. Los PR de nuevas funcionalidades enviados sin discusión previa serán cerrados. Al enviar un PR, por favor enlaza ese Issue.
3. **Mantén los cambios enfocados** - una funcionalidad o corrección por PR

### Lista de Verificación Pre-Commit

Antes de enviar, ejecuta siempre:

```bash
bun run lint       # Corregir problemas de linting
bun run format     # Formatear código
bun run typecheck  # Comprobar tipos
bun run build      # Verificar que la compilación tiene éxito
bun run test       # Ejecutar pruebas
```

Asegúrate de que:

1. Tus cambios logran la funcionalidad deseada.
2. Tus cambios no afectan negativamente a las funciones existentes.

---

## Estrategia de Pruebas

Seguimos una estrategia de pruebas basada en el ROI: **Prueba la lógica, no el DOM.**

1. **Imprescindible (Lógica)**: Servicios principales (Almacenamiento, Copia de seguridad), analizadores de datos y utilidades. Aquí se requiere TDD.
2. **Recomendable (Estado)**: Estado de UI complejo (ej: Reducer de carpetas).
3. **Omitir (Frágil)**: Manipulación directa del DOM (Content Scripts) y componentes de UI puros. Usa programación defensiva en su lugar.

---

## Enviar un Pull Request

### Pautas de PR

1. **Título**: Usa un título claro y descriptivo (ej: "feat: add dark mode toggle" o "fix: timeline scroll sync")
2. **Descripción**: Explica qué cambios hiciste y por qué
3. **Impacto en el Usuario**: Describe cómo se verán afectados los usuarios
4. **Prueba Visual (Estricto)**: Para CUALQUIER cambio de UI o nueva funcionalidad, **DEBES** proporcionar capturas de pantalla o grabaciones. **Sin captura = Sin revisión/respuesta.**
5. **Referencia de Problema**: Enlaza problemas relacionados (ej: "Closes #123")

### Formato de Mensaje de Commit

Sigue [Conventional Commits](https://www.conventionalcommits.org/):

- `feat:` - Nuevas funcionalidades
- `fix:` - Corrección de errores
- `docs:` - Cambios en documentación
- `chore:` - Tareas de mantenimiento
- `refactor:` - Refactorización de código
- `test:` - Agregar o actualizar pruebas

---

## Estilo de Código

### Pautas Generales

- **Prefiere retornos tempranos** sobre condicionales anidados
- **Usa nombres descriptivos** - evita abreviaciones
- **Evita números mágicos** - usa constantes con nombre
- **Sigue el estilo existente** - consistencia sobre preferencia

### Convenciones TypeScript

- **PascalCase**: Clases, interfaces, tipos, enums, componentes React
- **camelCase**: Funciones, variables, métodos
- **UPPER_SNAKE_CASE**: Constantes

### Orden de Importación

1. React e importaciones relacionadas
2. Bibliotecas de terceros
3. Importaciones absolutas internas (`@/...`)
4. Importaciones relativas (`./...`)
5. Importaciones solo de tipo

```typescript
import React, { useState } from 'react';

import { marked } from 'marked';

import { Button } from '@/components/ui/Button';
import { StorageService } from '@/core/services/StorageService';
import type { FolderData } from '@/core/types/folder';

import { parseData } from './parser';
```

---

## Agregar Soporte para Gem

Para agregar soporte para un nuevo Gem (Gems oficiales de Google o Gems personalizados):

1. Abre `src/pages/content/folder/gemConfig.ts`
2. Agrega una nueva entrada al array `GEM_CONFIG`:

```typescript
{
  id: 'your-gem-id',           // De la URL: /gem/your-gem-id/...
  name: 'Your Gem Name',       // Nombre para mostrar
  icon: 'material_icon_name',  // Icono de Google Material Symbols
}
```

### Encontrar el ID del Gem

- Abre una conversación con el Gem
- Verifica la URL: `https://gemini.google.com/app/gem/[GEM_ID]/...`
- Usa la parte `[GEM_ID]` en tu configuración

### Elegir un Icono

Usa nombres de iconos válidos de [Google Material Symbols](https://fonts.google.com/icons):

| Icono          | Caso de Uso            |
| -------------- | ---------------------- |
| `auto_stories` | Aprendizaje, Educación |
| `lightbulb`    | Ideas, Lluvia de ideas |
| `work`         | Carrera, Profesional   |
| `code`         | Programación, Técnica  |
| `analytics`    | Datos, Análisis        |

---

## Alcance del Proyecto

Voyager mejora la experiencia de chat de Gemini AI con:

- Navegación por línea de tiempo
- Organización de carpetas
- Bóveda de prompts
- Exportación de chat
- Personalización de UI

**Fuera de alcance**: Scraping de sitios, intercepción de red, automatización de cuentas.

---

## Obtener Ayuda

- 💬 [GitHub Discussions](https://github.com/Nagi-ovo/gemini-voyager/discussions) - Haz preguntas
- 🐛 [Issues](https://github.com/Nagi-ovo/gemini-voyager/issues) - Reporta errores
- 📖 [Documentación](https://gemini-voyager.vercel.app/) - Lee la documentación

---

## Licencia

Al contribuir, aceptas que tus contribuciones se licenciarán bajo la [Licencia GPLv3](../LICENSE).


================================================
FILE: .github/CONTRIBUTING_FR.md
================================================
# Guide de Contribution

> [!CAUTION]
> **Ce projet n'accepte actuellement PAS les PRs pour de nouvelles fonctionnalités.** Si vous souhaitez vraiment développer une fonctionnalité, veuillez suivre ce processus :
>
> 1. **Ouvrez d'abord un Issue** pour discuter de votre idée et de votre approche avec le mainteneur
> 2. **Attendez l'approbation et un plan d'implémentation solide** avant d'écrire du code ou de soumettre une PR
>
> Les PRs de nouvelles fonctionnalités soumises sans discussion préalable seront fermées sans examen. Merci de votre compréhension.

> [!IMPORTANT]
> **Statut du projet : Maintenance réduite.** Attendez-vous à des délais de réponse. Les PR avec tests sont prioritaires.

Merci d'envisager de contribuer à Voyager ! 🚀

Ce document fournit des directives et des instructions pour contribuer. Nous accueillons les corrections de bugs, les améliorations de la documentation et les traductions. Pour les nouvelles fonctionnalités, veuillez d'abord en discuter via un Issue.

## 🚫 Politique IA

**Nous rejetons explicitement les PR générées par l'IA qui n'ont pas été vérifiées manuellement.**

Bien que les outils d'IA soient d'excellents assistants, les contributions "paresseuses" de copier-coller font perdre du temps aux mainteneurs.

- **Les PR d'IA de mauvaise qualité** seront fermées immédiatement sans discussion.
- **Les PR sans explication** de la logique ou manquant de tests nécessaires seront rejetées.
- Vous devez comprendre et assumer la responsabilité de chaque ligne de code que vous soumettez.

## Table des Matières

- [Commencer](#commencer)
- [Réclamer un Ticket](#réclamer-un-ticket)
- [Configuration de Développement](#configuration-de-développement)
- [Apporter des Modifications](#apporter-des-modifications)
- [Soumettre une Pull Request](#soumettre-une-pull-request)
- [Style de Code](#style-de-code)
- [Ajouter le Support d'un Gem](#ajouter-le-support-dun-gem)
- [Licence](#licence)

---

## Commencer

### Prérequis

- **Bun** 1.0+ (Requis)
- Un navigateur basé sur Chromium pour les tests (Chrome, Edge, Brave, etc.)

### Démarrage Rapide

```bash
# Cloner le dépôt
git clone https://github.com/Nagi-ovo/gemini-voyager.git
cd gemini-voyager

# Installer les dépendances
bun install

# Démarrer le mode développement
bun run dev
```

---

## Réclamer un Ticket

Pour éviter le travail en double et coordonner les contributions :

### 1. Vérifier le Travail Existant

Avant de commencer, vérifiez si le ticket est déjà assigné à quelqu'un en regardant la section **Assignees**.

### 2. Réclamer un Ticket

Commentez `/claim` sur n'importe quel ticket non assigné pour vous l'assigner automatiquement. Un bot confirmera l'assignation.

### 3. Libérer si Nécessaire

Si vous ne pouvez plus travailler sur un ticket, commentez `/unclaim` pour le libérer pour d'autres.

### 4. Case à Cocher de Contribution

Lors de la création de tickets, vous pouvez cocher la case "I am willing to contribute code" pour indiquer votre intérêt à implémenter la fonctionnalité ou le correctif.

---

## Configuration de Développement

### Installer les Dépendances

```bash
bun install
```

### Commandes Disponibles

| Commande              | Description                                           |
| --------------------- | ----------------------------------------------------- |
| `bun run dev`         | Démarrer le mode dev Chrome avec rechargement à chaud |
| `bun run dev:firefox` | Démarrer le mode dev Firefox                          |
| `bun run dev:safari`  | Démarrer le mode dev Safari (macOS uniquement)        |
| `bun run build`       | Build de production pour Chrome                       |
| `bun run build:all`   | Build de production pour tous les navigateurs         |
| `bun run lint`        | Exécuter ESLint avec correction automatique           |
| `bun run typecheck`   | Exécuter la vérification de type TypeScript           |
| `bun run test`        | Exécuter la suite de tests                            |

### Charger l'Extension

1. Exécutez `bun run dev` pour démarrer le build de développement
2. Ouvrez Chrome et allez sur `chrome://extensions/`
3. Activez le "Mode développeur"
4. Cliquez sur "Charger l'extension non empaquetée" et sélectionnez le dossier `dist_chrome`

---

## Apporter des Modifications

### Avant de Commencer

1. **Créez une branche** depuis `main` :

   ```bash
   git checkout -b feature/nom-de-votre-fonctionnalite
   # ou
   git checkout -b fix/votre-correction-de-bug
   ```

2. **Lier les Issues** - Lors de l'implémentation d'une nouvelle fonctionnalité, vous devez **d'abord ouvrir un Issue pour discussion**. Les PR pour de nouvelles fonctionnalités soumises sans discussion préalable seront fermées. Lors de la soumission d'une PR, veuillez lier cet Issue.

3. **Gardez les modifications ciblées** - une fonctionnalité ou correction par PR

### Liste de Contrôle Pré-Commit

Avant de soumettre, exécutez toujours :

```bash
bun run lint       # Corriger les problèmes de linting
bun run format     # Formater le code
bun run typecheck  # Vérifier les types
bun run build      # Vérifier que le build réussit
bun run test       # Exécuter les tests
```

Assurez-vous que :

1. Vos modifications réalisent la fonctionnalité souhaitée.
2. Vos modifications n'affectent pas négativement les fonctionnalités existantes.

---

## Stratégie de Test

Nous suivons une stratégie de test basée sur le ROI : **Testez la logique, pas le DOM.**

1. **Indispensable (Logique)** : Services principaux (Stockage, Sauvegarde), analyseurs de données et utilitaires. Le TDD est requis ici.
2. **Recommandé (État)** : État d'interface utilisateur complexe (ex: Reducer de dossiers).
3. **Ignorer (Fragile)** : Manipulation directe du DOM (Scripts de contenu) et composants d'interface utilisateur purs. Utilisez plutôt la programmation défensive.

---

## Soumettre une Pull Request

### Directives de PR

1. **Titre** : Utilisez un titre clair et descriptif (ex: "feat: add dark mode toggle" ou "fix: timeline scroll sync")
2. **Description** : Expliquez quels changements vous avez effectués et pourquoi
3. **Impact Utilisateur** : Décrivez comment les utilisateurs seront affectés
4. **Preuve Visuelle (Strict)** : Pour TOUT changement d'interface ou nouvelle fonctionnalité, vous **DEVEZ** fournir des captures d'écran ou des enregistrements. **Pas de capture = Pas de revue/réponse.**
5. **Référence de Ticket** : Liez les tickets associés (ex: "Closes #123")

### Format du Message de Commit

Suivez [Conventional Commits](https://www.conventionalcommits.org/) :

- `feat:` - Nouvelles fonctionnalités
- `fix:` - Corrections de bugs
- `docs:` - Changements de documentation
- `chore:` - Tâches de maintenance
- `refactor:` - Refactorisation de code
- `test:` - Ajout ou mise à jour de tests

---

## Style de Code

### Directives Générales

- **Préférez les retours anticipés** aux conditionnelles imbriquées
- **Utilisez des noms descriptifs** - évitez les abréviations
- **Évitez les nombres magiques** - utilisez des constantes nommées
- **Respectez le style existant** - la cohérence prime sur la préférence

### Conventions TypeScript

- **PascalCase** : Classes, interfaces, types, énumérations, composants React
- **camelCase** : Fonctions, variables, méthodes
- **UPPER_SNAKE_CASE** : Constantes

### Ordre d'Importation

1. React et imports liés
2. Bibliothèques tierces
3. Imports absolus internes (`@/...`)
4. Imports relatifs (`./...`)
5. Imports de type uniquement

```typescript
import React, { useState } from 'react';

import { marked } from 'marked';

import { Button } from '@/components/ui/Button';
import { StorageService } from '@/core/services/StorageService';
import type { FolderData } from '@/core/types/folder';

import { parseData } from './parser';
```

---

## Ajouter le Support d'un Gem

Pour ajouter le support d'un nouveau Gem (Gems officiels Google ou Gems personnalisés) :

1. Ouvrez `src/pages/content/folder/gemConfig.ts`
2. Ajoutez une nouvelle entrée au tableau `GEM_CONFIG` :

```typescript
{
  id: 'votre-id-gem',          // Depuis l'URL : /gem/votre-id-gem/...
  name: 'Nom de Votre Gem',    // Nom d'affichage
  icon: 'material_icon_name',  // Nom de l'icône Google Material Symbols
}
```

### Trouver l'ID du Gem

- Ouvrez une conversation avec le Gem
- Vérifiez l'URL : `https://gemini.google.com/app/gem/[GEM_ID]/...`
- Utilisez la partie `[GEM_ID]` dans votre configuration

### Choisir une Icône

Utilisez des noms d'icônes valides de [Google Material Symbols](https://fonts.google.com/icons) :

| Icône          | Cas d'Utilisation        |
| -------------- | ------------------------ |
| `auto_stories` | Apprentissage, Éducation |
| `lightbulb`    | Idées, Brainstorming     |
| `work`         | Carrière, Professionnel  |
| `code`         | Programmation, Technique |
| `analytics`    | Données, Analyse         |

---

## Portée du Projet

Voyager améliore l'expérience de chat Gemini AI avec :

- Navigation par chronologie
- Organisation par dossiers
- Coffre-fort de prompts
- Exportation de chat
- Personnalisation de l'interface utilisateur

**Hors de portée** : Scraping de site, interception réseau, automatisation de compte.

---

## Obtenir de l'Aide

- 💬 [GitHub Discussions](https://github.com/Nagi-ovo/gemini-voyager/discussions) - Poser des questions
- 🐛 [Issues](https://github.com/Nagi-ovo/gemini-voyager/issues) - Signaler des bugs
- 📖 [Documentation](https://gemini-voyager.vercel.app/) - Lire la documentation

---

## Licence

En contribuant, vous acceptez que vos contributions soient licenciées sous la [Licence GPLv3](../LICENSE).


================================================
FILE: .github/CONTRIBUTING_JA.md
================================================
# 貢献ガイド

> [!CAUTION]
> **本プロジェクトは現在、新機能の PR を受け付けていません。** どうしても実装したい機能がある場合は、以下のプロセスに従ってください:
>
> 1. **まず Issue を作成して**、メンテナーとアイデアやアプローチについて議論してください
> 2. **承認と確実な実装計画が決まってから**、コードを書いて PR を提出してください
>
> 事前の議論なしに提出された新機能の PR は、レビューなしにクローズされます。ご理解のほどよろしくお願いいたします。

> [!IMPORTANT]
> **プロジェクトの状態: 低頻度メンテナンス。** 返信が遅れる可能性があります。テスト付きのPRが優先されます。

Voyager への貢献をご検討いただきありがとうございます!🚀

このドキュメントでは、貢献のためのガイドラインと手順を説明します。バグ修正、ドキュメントの改善、翻訳などの貢献を歓迎します。新機能については、まず Issue で議論してください。

## 🚫 AI ポリシー

**手動で検証されていない AI 生成の PR は明示的に拒否します。**

AI ツールは優れたアシスタントですが、「怠惰な」コピー&ペーストの貢献はメンテナの時間を浪費します。

- **低品質な AI PR** は、議論なしに即座にクローズされます。
- ロジックの**説明がない PR** や、必要なテストが不足している PR は拒否されます。
- あなたは提出するすべてのコード行を理解し、責任を負う必要があります。

## 目次

- [はじめに](#はじめに)
- [Issue の担当](#issue-の担当)
- [開発環境のセットアップ](#開発環境のセットアップ)
- [変更の実施](#変更の実施)
- [Pull Request の送信](#pull-request-の送信)
- [コードスタイル](#コードスタイル)
- [Gem サポートの追加](#gem-サポートの追加)
- [ライセンス](#ライセンス)

---

## はじめに

### 前提条件

- **Bun** 1.0+(必須)
- テスト用の Chromium ベースのブラウザ(Chrome, Edge, Brave など)

### クイックスタート

```bash
# リポジトリをクローン
git clone https://github.com/Nagi-ovo/gemini-voyager.git
cd gemini-voyager

# 依存関係をインストール
bun install

# 開発モードを開始
bun run dev
```

---

## Issue の担当

重複作業を避け、貢献を調整するために:

### 1. 既存の作業を確認

開始する前に、Issue の **Assignees** セクションを見て、すでに誰かが担当していないか確認してください。

### 2. Issue を担当する

未割り当ての Issue に `/claim` とコメントすると、自動的にあなた自身が担当者に割り当てられます。ボットが割り当てを確認します。

### 3. 必要に応じて担当を解除

Issue に取り組めなくなった場合は、`/unclaim` とコメントして、他の人のために解放してください。

### 4. 貢献のチェックボックス

Issue を作成する際、「I am willing to contribute code」チェックボックスをオンにして、機能の実装や修正に興味があることを示すことができます。

---

## 開発環境のセットアップ

### 依存関係のインストール

```bash
bun install
```

### 利用可能なコマンド

| コマンド              | 説明                                      |
| --------------------- | ----------------------------------------- |
| `bun run dev`         | Chrome 開発モードを開始(ホットリロード) |
| `bun run dev:firefox` | Firefox 開発モードを開始                  |
| `bun run dev:safari`  | Safari 開発モードを開始(macOS のみ)     |
| `bun run build`       | Chrome 用のプロダクションビルド           |
| `bun run build:all`   | 全ブラウザ用のプロダクションビルド        |
| `bun run lint`        | ESLint を実行して自動修正                 |
| `bun run typecheck`   | TypeScript の型チェックを実行             |
| `bun run test`        | テストスイートを実行                      |

### 拡張機能の読み込み

1. `bun run dev` を実行して開発ビルドを開始します
2. Chrome を開き、`chrome://extensions/` に移動します
3. 「デベロッパー モード」を有効にします
4. 「パッケージ化されていない拡張機能を読み込む」をクリックし、`dist_chrome` フォルダを選択します

---

## 変更の実施

### 作業を始める前に

1. `main` から**ブランチを作成**します:

   ```bash
   git checkout -b feature/your-feature-name
   # または
   git checkout -b fix/your-bug-fix
   ```

2. **Issue をリンクする** - 新機能の実装については、**まず議論のために Issue を提出する必要があります**。事前の議論なしに提出された新機能の PR はクローズされます。PR を送信する際は、その Issue をリンクしてください。

3. **変更を集中させる** - PR ごとに1つの機能または修正

### コミット前チェックリスト

送信する前に、必ず以下を実行してください:

```bash
bun run lint       # リンティングの問題を修正
bun run format     # コードの整形
bun run typecheck  # 型をチェック
bun run build      # ビルドが成功することを確認
bun run test       # テストを実行
```

以下を確認してください:

1. 変更内容が期待通りに機能すること。
2. 既存の機能に影響を与えていないこと。

---

## テスト戦略

私たちは「ROI に基づく」テスト戦略に従います:**DOM ではなくロジックをテストしてください。**

1. **必須 (Logic)**: コアサービス (ストレージ、バックアップ)、データパーサー、ユーティリティ。ここでは TDD が必須です。
2. **推奨 (State)**: 複雑な UI 状態 (例: フォルダ Reducer)。
3. **スキップ (Fragile)**: 直接的な DOM 操作 (Content Scripts) や純粋な UI コンポーネント。代わりに防御的プログラミングを使用してください。

---

## Pull Request の送信

### PR ガイドライン

1. **タイトル**: 明確で説明的なタイトルを使用してください(例:"feat: add dark mode toggle" または "fix: timeline scroll sync")
2. **説明**: どのような変更を行ったか、およびその理由を説明してください
3. **ユーザーへの影響**: ユーザーにどのような影響があるかを説明してください
4. **視覚的証拠 (厳格)**: UI の変更や新機能については、**必ず**スクリーンショットまたは画面録画を提供してください。**スクリーンショットなし = レビュー/返信しません。**
5. **Issue の参照**: 関連する Issue をリンクしてください(例:"Closes #123")

### コミットメッセージの形式

[Conventional Commits](https://www.conventionalcommits.org/) に従ってください:

- `feat:` - 新機能
- `fix:` - バグ修正
- `docs:` - ドキュメントの変更
- `chore:` - メンテナンス作業
- `refactor:` - コードのリファクタリング
- `test:` - テストの追加または更新

---

## コードスタイル

### 一般的なガイドライン

- ネストされた条件分岐よりも**早期リターンを優先**
- **説明的な名前を使用** - 略語は避ける
- **マジックナンバーを避ける** - 名前付き定数を使用
- **既存のスタイルに合わせる** - 好みよりも一貫性

### TypeScript の規約

- **PascalCase**: クラス、インターフェース、型、Enum、React コンポーネント
- **camelCase**: 関数、変数、メソッド
- **UPPER_SNAKE_CASE**: 定数

### インポートの順序

1. React および関連するインポート
2. サードパーティライブラリ
3. 内部の絶対インポート(`@/...`)
4. 相対インポート(`./...`)
5. 型のみのインポート

```typescript
import React, { useState } from 'react';

import { marked } from 'marked';

import { Button } from '@/components/ui/Button';
import { StorageService } from '@/core/services/StorageService';
import type { FolderData } from '@/core/types/folder';

import { parseData } from './parser';
```

---

## Gem サポートの追加

新しい Gem(公式 Google Gems またはカスタム Gems)のサポートを追加するには:

1. `src/pages/content/folder/gemConfig.ts` を開きます
2. `GEM_CONFIG` 配列に新しいエントリを追加します:

```typescript
{
  id: 'your-gem-id',           // URL から取得: /gem/your-gem-id/...
  name: 'Your Gem Name',       // 表示名
  icon: 'material_icon_name',  // Google Material Symbols アイコン
}
```

### Gem ID の見つけ方

- Gem との会話を開きます
- URL を確認します: `https://gemini.google.com/app/gem/[GEM_ID]/...`
- 設定で `[GEM_ID]` の部分を使用します

### アイコンの選択

有効な [Google Material Symbols](https://fonts.google.com/icons) アイコン名を使用してください:

| アイコン       | 使用例               |
| -------------- | -------------------- |
| `auto_stories` | 学習、教育           |
| `lightbulb`    | アイデア、ブレスト   |
| `work`         | キャリア、専門職     |
| `code`         | プログラミング、技術 |
| `analytics`    | データ、分析         |

---

## プロジェクトの範囲

Voyager は、以下の機能で Gemini AI チャット体験を向上させます:

- タイムラインナビゲーション
- フォルダ整理
- プロンプトヴォルト
- チャットのエクスポート
- UI カスタマイズ

**範囲外**: サイトのスクレイピング、ネットワーク傍受、アカウントの自動化。

---

## ヘルプを得る

- 💬 [GitHub Discussions](https://github.com/Nagi-ovo/gemini-voyager/discussions) - 質問する
- 🐛 [Issues](https://github.com/Nagi-ovo/gemini-voyager/issues) - バグを報告する
- 📖 [ドキュメント](https://gemini-voyager.vercel.app/) - ドキュメントを読む

---

## ライセンス

貢献することにより、あなたの貢献が [GPLv3 ライセンス](../LICENSE) の下でライセンスされることに同意したものとみなされます。


================================================
FILE: .github/FUNDING.yml
================================================
github: Nagi-ovo
buy_me_a_coffee: Nag1ovo
custom: ['https://afdian.com/a/nagi-ovo']


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: '🐛 反馈缺陷 Bug Report'
description: '反馈一个问题缺陷 | Report a bug'
title: '[Bug] '
labels: '🐛 Bug'
body:
  - type: markdown
    attributes:
      value: |
        ### ⚠️ 项目维护状态 | Maintenance Status

        **本项目目前处于低频维护模式。 | This project is currently in Low Maintenance mode.**

        - 我们将优先处理带有测试代码的 Pull Request,而非 Issue。 | We prioritize Pull Requests with tests over Issues.
        - 对于未提供清晰复现步骤或环境信息的非核心 Bug,可能会被直接关闭。 | Non-critical bugs without minimal reproduction steps may be closed directly.
        - 维护者精力有限,回复可能会有显著延迟,请谅解。 | Please be patient as response times will be significantly delayed.
  - type: checkboxes
    id: checklist
    attributes:
      label: '⚠️ 前置检查 | Essential Checklist'
      description: '请确认您已完成以下操作 | Please confirm you have done the following'
      options:
        - label: '我已在 Issue 中搜索过类似问题,确认这不是一个重复的问题 / I have searched existing issues'
          required: true
        - label: '我理解如果未提供清晰的复现步骤或环境信息,Issue 可能会被关闭 / I understand my issue may be closed if repro steps are missing'
          required: true
        - label: '我确认使用的是插件的最新版本 / I am using the latest version of the extension'
          required: true
  - type: dropdown
    attributes:
      label: '💻 系统环境 | Operating System'
      options:
        - Windows
        - macOS
        - Ubuntu
        - Other Linux
        - Other
    validations:
      required: true
  - type: dropdown
    attributes:
      label: '🌐 浏览器 | Browser'
      description: 'Which browser are you using?'
      options:
        - Chrome
        - Edge
        - Safari
        - Firefox
        - Other
    validations:
      required: true
  - type: input
    attributes:
      label: '📦 扩展版本 | Extension Version'
      description: 'Which version of Gemini Voyager are you using? (e.g., 1.1.3)'
      placeholder: '1.1.3'
    validations:
      required: true
  - type: textarea
    attributes:
      label: '🐛 问题描述 | Bug Description'
      description: 'A clear and concise description of the bug.'
      placeholder: 'Describe what happened...'
    validations:
      required: true
  - type: textarea
    attributes:
      label: '🚦 期望结果 | Expected Behavior'
      description: 'A clear and concise description of what you expected to happen.'
      placeholder: 'Describe what you expected...'
  - type: textarea
    attributes:
      label: '📷 复现步骤 | Recurrence Steps'
      description: 'Steps to reproduce the behavior.'
      placeholder: |
        1. Go to '...'
        2. Click on '...'
        3. See error
  - type: textarea
    attributes:
      label: '📸 截图 | Screenshots'
      description: 'REQUIRED for UI issues. If we cannot see it, we will not fix it. / UI 问题必填。如果看不到问题,我们将不会修复。'
      placeholder: 'Drag and drop images here or paste image URLs'
  - type: textarea
    attributes:
      label: '📝 补充信息 | Additional Information'
      description: 'If your problem needs further explanation, please add more information here.'
      placeholder: 'Any additional context, error messages, or console logs...'
  - type: checkboxes
    attributes:
      label: '💻 贡献意愿 | Contribution'
      options:
        - label: '我愿意为该问题贡献修复代码 / I am willing to contribute a fix for this bug'
        - label: '💡 其他贡献者可评论 `/claim` 认领此 Issue / Other contributors can comment `/claim` to claim'


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: '💬 讨论区 Discussions'
    url: https://github.com/Nagi-ovo/gemini-voyager/discussions
    about: '讨论想法或提问,而非提交 Bug 或 Feature | For general discussions, questions, and ideas'


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: '🌠 功能需求 Feature Request'
description: '需求或建议 | Suggest an idea for Gemini Voyager'
title: '[Feature] '
labels: '🌠 Feature Request'
body:
  - type: markdown
    attributes:
      value: |
        ### ⚠️ 项目维护状态 | Maintenance Status

        **本项目目前处于低频维护模式。 | This project is currently in Low Maintenance mode.**

        - 无法保证能够投入精力,及时实现新的功能需求。 | The maintainer cannot guarantee they will be able to dedicate time to implement new features in a timely manner.
        - 我们非常**欢迎并鼓励社区贡献**! | However, we **welcome and encourage community contributions**!
        - 如果您希望该功能被实现,提交高质量的 Pull Request 是最佳途径。但在提交 PR 前,**请务必先在此 Issue 中进行讨论**。未经讨论直接提交的新功能 PR 将被关闭。 | The best way to see this feature realized is to submit a Pull Request. However, before submitting a PR, **you MUST first discuss it in this Issue**. PRs for new features submitted without prior discussion will be closed.
  - type: textarea
    attributes:
      label: '🥰 需求描述 | Feature Description'
      description: 'Please add a clear and concise description of the problem you are seeking to solve with this feature request.'
      placeholder: "Is your feature request related to a problem? Please describe.\nExample: I'm always frustrated when..."
    validations:
      required: true
  - type: textarea
    attributes:
      label: '🧐 解决方案 | Proposed Solution'
      description: "Describe the solution you'd like in a clear and concise manner."
      placeholder: 'Describe what you want to happen...'
    validations:
      required: true
  - type: textarea
    attributes:
      label: '🔄 替代方案 | Alternatives Considered'
      description: 'Describe any alternative solutions or features you have considered.'
      placeholder: 'Describe alternative solutions...'
  - type: textarea
    attributes:
      label: '📝 补充信息 | Additional Information'
      description: 'REQUIRED for UI features: Please provide mockups or screenshots. / UI 功能必填:请提供设计图或截图。'
      placeholder: 'No screenshot = No reply (for UI features).'
  - type: checkboxes
    attributes:
      label: '💻 贡献意愿 | Contribution'
      options:
        - label: '我愿意为该功能贡献代码 / I am willing to contribute code for this feature'
        - label: '💡 其他贡献者可评论 `/claim` 认领此 Issue / Other contributors can comment `/claim` to claim'


================================================
FILE: .github/README_AR.md
================================================
<div align="center" dir="rtl">
  <img src="../docs/public/assets/promotion/Promo-Banner.png" alt="promotion"/>
  <h3>اجعل تجربتك مع Gemini™ ملكك حقاً ✨</h3>
  <p>
    تصفح المحادثات بجدول زمني أنيق، ونظم الدردشات في مجلدات، وقم ببناء مستودع المطالبات الخاص بك.<br>
    <b>إنه الترقية المفقودة لـ Google Gemini.</b>
  </p>
  
  <p>
    <img src="https://img.shields.io/badge/Chrome-✓-4285F4?style=flat-square&logo=googlechrome&logoColor=white" alt="Chrome">
    <img src="https://img.shields.io/badge/Edge-✓-0078D7?style=flat-square&logo=microsoftedge&logoColor=white" alt="Edge">
    <img src="https://img.shields.io/badge/Firefox-✓-FF7139?style=flat-square&logo=firefox&logoColor=white" alt="Firefox">
    <img src="https://img.shields.io/badge/Safari-✓-000000?style=flat-square&logo=safari&logoColor=white" alt="Safari">
    <img src="https://img.shields.io/badge/Opera-✓-FF1B2D?style=flat-square&logo=opera&logoColor=white" alt="Opera">
    <img src="https://img.shields.io/badge/Brave-✓-FB542B?style=flat-square&logo=brave&logoColor=white" alt="Brave">
  </p>
  <p>
    <img src="https://img.shields.io/github/stars/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub stars">
    <img src="https://img.shields.io/github/forks/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub forks">
    <img src="https://img.shields.io/github/v/release/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="Latest version">
    <img src="https://img.shields.io/github/downloads/Nagi-ovo/gemini-voyager/total?style=flat-square&logo=github" alt="GitHub downloads">
    <img src="https://img.shields.io/chrome-web-store/users/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome Web Store users">
    <img src="https://img.shields.io/chrome-web-store/rating/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome Web Store rating">
    <img src="https://img.shields.io/amo/users/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox Add-ons users">
    <img src="https://img.shields.io/amo/rating/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox Add-ons rating">
  </p>
  <p>
    <a href="https://trendshift.io/repositories/16094" target="_blank"><img src="https://trendshift.io/api/badge/repositories/16094" alt="Nagi-ovo%2Fgemini-voyager | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
    <a href="https://www.producthunt.com/products/gemini-voyager?embed=true&amp;utm_source=badge-featured&amp;utm_medium=badge&amp;utm_campaign=badge-gemini-voyager" target="_blank" rel="noopener noreferrer"><img alt="Gemini Voyager - All-in-one Gemini suite: folders, chat export and much more | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1064704&amp;theme=light&amp;t=1768842096186"></a>
  </p>
  <p align="center">
    ✨ نحن متواجدون الآن على Product Hunt! يسعدنا سماع آرائكم. ❤️
  </p>
</div>

<p align="center">
  <a href="https://voyager.nagi.fun/ar">📖 التوثيق</a> • 
  <a href="../README.md">English</a> • 
  <a href="./README_ZH.md">简体中文</a> •
  <a href="./README_ZH_TW.md">繁體中文</a> •
  <a href="./README_JA.md">日本語</a> •
  <a href="./README_FR.md">Français</a> •
  <a href="./README_ES.md">Español</a> •
  <a href="./README_PT.md">Português</a> •
  <a href="./README_RU.md">Русский</a> •
  <a href="./README_KO.md">한국어</a>
</p>

<p align="center">
    <img src="https://count.getloli.com/@gemini-voyager?name=gemini-voyager&theme=rule34&padding=7&offset=0&align=top&scale=1&pixelated=1&darkmode=auto" width="400">
  </p>

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024507762483277927?s=20" target="_blank">
    <img src="../docs/public/assets/x-recommendation.png" alt="KOL Recommendation" width="500">
  </a>
  <br>
  <b>🎉 نوصي به بشدة من قبل كبار المؤثرين في مجال التكنولوجيا والمجتمع!</b>
</p>

> [!IMPORTANT]
> **إشعار تغيير الاسم**: بسبب مخاوف تتعلق بالعلامات التجارية وحقوق النشر، تم تغيير اسم هذا الامتداد رسمياً إلى **Voyager**. ومع ذلك، بسبب بطء شديد في عملية مراجعة Chrome Web Store، لم يتم الموافقة على تغيير الاسم خلال 7 أيام — وهو غير متاح مؤقتاً على Chrome Web Store.

> [!NOTE]
> إذا كان Voyager مفيداً لك، فشاركْه على X أو Reddit أو YouTube إلخ. كل مشاركة تساعد المزيد من الناس على اكتشاف المشروع وتحسين تجربة Gemini. شكراً.

---

## 👋 لماذا Voyager؟

نحن نحب Gemini، لكننا تمنينا أحياناً لو كان لديه المزيد من "التنظيم".

لهذا السبب قمنا ببناء **Voyager**. إنه ليس مجرد أداة؛ إنه رفيق يساعدك في الحفاظ على تنظيم محادثاتك مع الذكاء الاصطناعي وجعلها سهلة الوصول ومنتجة. سواء كنت باحثاً تدير عشرات الخيوط، أو مطوراً يحفظ مقتطفات برمجية، أو مجرد شخص يحب النظام، فإن Voyager مصمم لك.

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024509398601597412?s=20" target="_blank">
    <img src="../docs/public/assets/try-voyager.png" alt="Try Voyager" width="500">
  </a>
  <br>
  <i>خلال المشكلة التي حدثت في 18 فبراير حيث تسبب تطبيق Google Gemini في جعل المحادثات التاريخية لبعض المستخدمين غير قابلة للوصول، تمكن مستخدمو Voyager من الاستمرار في رؤية محادثاتهم المحفوظة في مجلداتهم.</i>
</p>

---

## ✨ الميزات

### 🌌 الميزات الأساسية (Gemini & AI Studio)

- **📂 [تنظيم المجلدات](https://voyager.nagi.fun/ar/guide/folders)**: نظم دردشاتك في تسلسل هرمي للمجلدات مع دعم **السحب والإفلات** و **مزامنة Google Drive**.
  - **Gemini**: يدعم **وضع عزل الحساب** و **ألوان المجلدات المخصصة**.
- **💡 [مستودع المطالبات](https://voyager.nagi.fun/ar/guide/prompts)**: احفظ وأعد استخدام أفضل مطالباتك في Gemini و AI Studio و[المواقع المخصصة](https://voyager.nagi.fun/ar/guide/custom-websites).
- **☁️ [المزامنة السحابية](https://voyager.nagi.fun/ar/guide/cloud-sync)**: قم بمزامنة المجلدات ومستودع المطالبات مع Google Drive.
- **📐 نسخ الصيغ**: نسخ بنقرة واحدة لأكواد المصدر LaTeX و MathML (Word).
- **🌦️ التأثيرات البصرية**: أضف أجواء موسمية مع **الثلج** أو **المطر السينمائي** أو **بتلات الساكورا المتساقطة** من لوحة الإعدادات.

### ✨ ميزات Gemini الحصرية

- **📍 [تصفح الجدول الزمني](https://voyager.nagi.fun/ar/guide/timeline)**: عقد بصرية للتنقل بين الرسائل، وتمييز اللحظات الرئيسية، وإدارة فروع المحادثة.
- **💾 [تصدير الدردشة](https://voyager.nagi.fun/ar/guide/export)**: صَدّر المحادثات إلى تنسيقات JSON أو Markdown أو PDF مع تضمين الصور.
- **🧜‍♀️ [رسم Mermaid](https://voyager.nagi.fun/ar/guide/mermaid)**: عرض تلقائي للمخططات الانسيابية ومخططات التتابع وغيرها من رسوم Mermaid.
- **📝 [إصلاح عرض Markdown](https://voyager.nagi.fun/ar/guide/markdown-fix)**: إصلاح تلقائي لتنسيق Markdown العريض الذي تعطل بسبب عناصر HTML التي أدرجها Gemini.
- **🍌 [NanoBanana](https://voyager.nagi.fun/ar/guide/nanobanana)**: إزالة العلامة المائية بدون فقدان الجودة للصور التي ينتجها Gemini.
- **🔬 [البحث العميق](https://voyager.nagi.fun/ar/guide/deep-research)**: استخرج عمليات التفكير وروابط البحث من جلسات البحث العميق.
- **🛠️ أدوات القوة**:
  - **[الحذف الجماعي](https://voyager.nagi.fun/ar/guide/batch-delete)**: تنظيف السجل الخاص بك دفعة واحدة.
  - **[الرد مع اقتباس](https://voyager.nagi.fun/ar/guide/quote-reply)**: الرد مع السياق عن طريق تحديد النص.
  - **[مزامنة عنوان علامة التبويب](https://voyager.nagi.fun/ar/guide/tab-title)**: مزامنة عنوان علامة تبويب المتصفح تلقائياً مع عنوان الدردشة.
  - **[منع التمرير التلقائي](https://voyager.nagi.fun/ar/guide/prevent-auto-scroll)**: يمنع الصفحة من النزول تلقائياً إلى الأسفل.
  - **[طي الإدخال](https://voyager.nagi.fun/ar/guide/input-collapse)**: منطقة إدخال قابلة للطي تلقائياً لتوفير مساحة قراءة أكبر.
  - **[النموذج الافتراضي](https://voyager.nagi.fun/ar/guide/default-model)**: تعيين النموذج المفضل لديك كنموذج افتراضي.
  - **[إخفاء العناصر الأخيرة والـ Gems](https://voyager.nagi.fun/ar/guide/recents-hider)**: إخفاء قائمة "الأخيرة" في الشريط الجانبي لتقليل التشتت.

### 🎨 التخصيص

- افتح نافذة الإضافة المنبثقة وابحث عن **التأثيرات البصرية** للتبديل بين `إيقاف`، `ثلج`، `ساكورا`، و`مطر`.
- التأثيرات تُعرض كطبقات خفيفة بملء الشاشة ولا تعيق التفاعل مع الصفحة.
- عند تبديل التأثيرات أو إيقافها، تتلاشى الجسيمات بشكل طبيعي بدلاً من الاختفاء المفاجئ.

---

## 📥 التثبيت

> ⚠️ ملاحظة: مدير المطالبات هو الميزة الوحيدة التي تدعم Gemini للمؤسسات.

<div align="center">
  <a href="https://chromewebstore.google.com/detail/kjdpnimcnfinmilocccippmododhceol?utm_source=github&utm_medium=readme&utm_campaign=organic_growth&utm_content=ar" target="_blank">
    <img src="https://img.shields.io/badge/Chrome%20Web%20Store-4285F4?style=for-the-badge&logo=googlechrome&logoColor=white" alt="Chrome Web Store" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://microsoftedge.microsoft.com/addons/detail/gemini-voyager/gibmkggjijalcjinbdhcpklodjkhhlne" target="_blank">
    <img src="https://img.shields.io/badge/Microsoft%20Edge-0078D7?style=for-the-badge&logo=microsoftedge&logoColor=white" alt="Microsoft Edge Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://addons.mozilla.org/firefox/addon/gemini-voyager/" target="_blank">
    <img src="https://img.shields.io/badge/Firefox%20Add--ons-FF7139?style=for-the-badge&logo=firefox&logoColor=white" alt="Firefox Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://github.com/Nagi-ovo/gemini-voyager/releases/latest/" target="_blank">
    <img src="https://img.shields.io/badge/Safari-000000?style=for-the-badge&logo=safari&logoColor=white" alt="Safari تنزيل" height="36">
  </a>
</div>

<p align="center">
  <sub>سوق <b>Chrome</b> الإلكتروني يعمل أيضاً على Edge و Opera و Brave و Vivaldi و Arc ومتصفحات Chromium الأخرى.</sub>
</p>

> **حالة المتجر:** Chrome ✅ · Firefox ✅ · Edge ✅ · Safari ✅

لـ **التثبيت اليدوي** أو **بناء التطوير**، يرجى الرجوع إلى [دليل التثبيت](https://voyager.nagi.fun/ar/guide/installation).

---

## ☕ دعم هذا المشروع

<div align="center">
  <a href="https://github.com/Nagi-ovo/gemini-voyager">
    <img src="https://raw.githubusercontent.com/Nagi-ovo/gemini-voyager/main/docs/public/assets/sponsors.svg" width="1000px" />
  </a>
</div>

إذا كان Voyager يسهل حياتك، فكر في دعوتي لتناول القهوة. يساعد ذلك في استمرار التحديثات! سيتم إدراج الداعمين في قسم الشكر الخاص بنا. ❤️

<div align="center">
  <a href="https://www.buymeacoffee.com/Nag1ovo" target="_blank">
    <img src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" height="36">
  </a>
  <a href="https://github.com/sponsors/Nagi-ovo" target="_blank">
    <img src="https://img.shields.io/badge/Sponsor%20me-GitHub-ea4aaa?style=for-the-badge&logo=github&logoColor=white" alt="Sponsor on GitHub" height="36">
  </a>
  
  <p><b>أو الدعم عبر WeChat / Alipay / Afdian:</b></p>
  <table align="center" border="0" cellpadding="0" cellspacing="0">
    <tr>
      <td align="center">
        <img src="../docs/public/assets/wechat-sponsor.png" alt="WeChat Pay" height="160"><br>
        <sub><b>WeChat Pay</b></sub>
      </td>
      <td align="center">
        <img src="../docs/public/assets/alipay-sponsor.jpg" alt="Alipay" height="160"><br>
        <sub><b>Alipay</b></sub>
      </td>
      <td align="center">
        <a href="https://afdian.com/a/nagi-ovo" target="_blank">
          <picture>
            <source media="(prefers-color-scheme: dark)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo&bg_color=%230d1117&text_color=%23dedbd7&border_color=%232e343d" />
            <source media="(prefers-color-scheme: light)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" />
            <img alt="Nagi-ovo's Profile" src="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" height="160" />
          </picture>
        </a><br>
        <sub><b>Afdian</b></sub>
      </td>
    </tr>
  </table>
</div>

### 🎙️ أداة موصى بها: Typeless

أوصي بشدة بـ **[Typeless (typeless.com)](https://www.typeless.com/?via=gemini-voyager)**، وهي أداة تحويل الصوت إلى نص بالذكاء الاصطناعي استخدمتها على نطاق واسع أثناء تطوير Voyager. لقد وفرت لي الكثير من الوقت وزادت من إنتاجيتي بشكل كبير.

> 🎁 **[انضم عبر رابط الإحالة الخاص بي](https://www.typeless.com/?via=gemini-voyager)** (الكود: _`gemini-voyager`_) للحصول على **5 دولارات رصيد مجاني**. ❤️

---

## 🤝 المساهمة والتطوير

[<img src="https://devin.ai/assets/askdeepwiki.png" alt="Ask DeepWiki" height="20"/>](https://deepwiki.com/Nagi-ovo/gemini-voyager)

نرحب بالمساهمات!

- **Issues**: استخدم نماذجنا لـ [تقرير الأخطاء](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/bug_report.md) أو [طلب الميزات](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/feature_request.yml).
- **Pull Requests**: تحقق من [CONTRIBUTING.md](./CONTRIBUTING.md).

شكراً لمساعدتك في جعل Voyager أفضل! ❤️

### ❤️ شكر خاص

شكر خاص لجميع المساهمين على مساهماتهم في Voyager ❤️

<a href="https://github.com/Nagi-ovo/gemini-voyager/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=Nagi-ovo/gemini-voyager&max=200&columns=14" />
</a>

---

## 🌟 الاعتمادات

- **[DeepSeek Voyager](https://github.com/Azurboy/deepseek-voyager)** - نسخة مشتقة من Voyager مخصصة لـ DeepSeek.

- **[claude-nexus](https://github.com/Qiuner/claude-nexus)** - إضافة لتحسين Claude.ai مستوحاة من Voyager، تتضمن التنقّل عبر الخط الزمني، وإدارة المجلدات، ومكتبة المطالبات والمزيد، مع توافق كامل لاستيراد/تصدير المطالبات مع Voyager!

- **[ChatGPT Conversation Timeline](https://github.com/Reborn14/chatgpt-conversation-timeline)** - مصدر الإلهام الأصلي للتنقل في الجدول الزمني للمحادثة.

- **[Ophel Atlas](https://github.com/urzeye/ophel)** - إضافة متصفح تحوّل محادثات الذكاء الاصطناعي إلى مستندات منظمة وقابلة للبحث، مع إنشاء تلقائي للمخططات وإدارة المحادثات ومكتبة للأوامر، وتدعم منصات ذكاء اصطناعي متعددة.

---

<div align="center">
  <a href="https://www.star-history.com/#Nagi-ovo/gemini-voyager&type=date&legend=top-left">
   <picture>
     <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&theme=dark&legend=top-left" />
     <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
     <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
   </picture>
  </a>
  <p>صنع بكل ❤️ بواسطة Jesse Zhang</p>
  <sub>رخصة GPLv3 © 2025</sub>
</div>


================================================
FILE: .github/README_ES.md
================================================
<div align="center">
  <img src="../docs/public/assets/promotion/Promo-Banner.png" alt="promotion"/>
  <h3>Haz que tu experiencia con Gemini™ sea verdaderamente tuya ✨</h3>
  <p>
    Navegación elegante por línea de tiempo, organización de chats con carpetas y tu propio depósito de prompts.<br>
    <b>Es la pieza que le faltaba a Google Gemini.</b>
  </p>
  
  <p>
    <img src="https://img.shields.io/badge/Chrome-✓-4285F4?style=flat-square&logo=googlechrome&logoColor=white" alt="Chrome">
    <img src="https://img.shields.io/badge/Edge-✓-0078D7?style=flat-square&logo=microsoftedge&logoColor=white" alt="Edge">
    <img src="https://img.shields.io/badge/Firefox-✓-FF7139?style=flat-square&logo=firefox&logoColor=white" alt="Firefox">
    <img src="https://img.shields.io/badge/Safari-✓-000000?style=flat-square&logo=safari&logoColor=white" alt="Safari">
    <img src="https://img.shields.io/badge/Opera-✓-FF1B2D?style=flat-square&logo=opera&logoColor=white" alt="Opera">
    <img src="https://img.shields.io/badge/Brave-✓-FB542B?style=flat-square&logo=brave&logoColor=white" alt="Brave">
  </p>
  <p>
    <img src="https://img.shields.io/github/stars/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub stars">
    <img src="https://img.shields.io/github/forks/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub forks">
    <img src="https://img.shields.io/github/v/release/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="Latest version">
    <img src="https://img.shields.io/github/downloads/Nagi-ovo/gemini-voyager/total?style=flat-square&logo=github" alt="GitHub downloads">
    <img src="https://img.shields.io/chrome-web-store/users/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome Web Store users">
    <img src="https://img.shields.io/chrome-web-store/rating/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome Web Store rating">
    <img src="https://img.shields.io/amo/users/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox Add-ons users">
    <img src="https://img.shields.io/amo/rating/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox Add-ons rating">
  </p>
  <p>
    <a href="https://trendshift.io/repositories/16094" target="_blank"><img src="https://trendshift.io/api/badge/repositories/16094" alt="Nagi-ovo%2Fgemini-voyager | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
    <a href="https://www.producthunt.com/products/gemini-voyager?embed=true&amp;utm_source=badge-featured&amp;utm_medium=badge&amp;utm_campaign=badge-gemini-voyager" target="_blank" rel="noopener noreferrer"><img alt="Gemini Voyager - All-in-one Gemini suite: folders, chat export and much more | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1064704&amp;theme=light&amp;t=1768842096186"></a>
  </p>
  <p align="center">
    ✨ ¡Estamos en Product Hunt! Nos encantaría conocer tu opinión. ❤️
  </p>
</div>

<p align="center">
  <a href="https://voyager.nagi.fun/es">📖 Documentación</a> • 
  <a href="../README.md">English</a> • 
  <a href="./README_ZH.md">简体中文</a> •
  <a href="./README_ZH_TW.md">繁體中文</a> •
  <a href="./README_JA.md">日本語</a> •
  <a href="./README_FR.md">Français</a> •
  <a href="./README_PT.md">Português</a> •
  <a href="./README_RU.md">Русский</a> •
  <a href="./README_AR.md">العربية</a> •
  <a href="./README_KO.md">한국어</a>
</p>

<p align="center">
    <img src="https://count.getloli.com/@gemini-voyager?name=gemini-voyager&theme=rule34&padding=7&offset=0&align=top&scale=1&pixelated=1&darkmode=auto" width="400">
  </p>

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024507762483277927?s=20" target="_blank">
    <img src="../docs/public/assets/x-recommendation.png" alt="KOL Recommendation" width="500">
  </a>
  <br>
  <b>🎉 ¡Altamente recomendado por los principales influencers tecnológicos!</b>
</p>

> [!IMPORTANT]
> **Aviso de cambio de nombre**: Debido a problemas de marcas y derechos de autor, esta extensión ha sido renombrada oficialmente a **Voyager**. Sin embargo, debido a la lentitud del proceso de revisión de Chrome Web Store, el cambio de nombre no fue aprobado en 7 días — está temporalmente no disponible en Chrome Web Store.

> [!NOTE]
> Si Voyager te resulta útil, compártelo en X, Reddit, YouTube, etc. Cada difusión ayuda a que más personas descubran el proyecto y mejoren la experiencia con Gemini. Gracias.

---

## 👋 ¿Por qué Voyager?

Nos encanta Gemini, pero a veces desearíamos que tuviera un poco más de estructura.

Por eso creamos **Voyager**. No es solo una herramienta; es un compañero que te ayuda a mantener tus conversaciones con IA organizadas, accesibles y productivas. Ya seas un investigador que maneja docenas de hilos, un desarrollador que guarda fragmentos de código, o simplemente alguien que ama el orden, Voyager está diseñado para ti.

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024509398601597412?s=20" target="_blank">
    <img src="../docs/public/assets/try-voyager.png" alt="Try Voyager" width="500">
  </a>
  <br>
  <i>Durante el problema del 18 de febrero en el que la aplicación Google Gemini hizo inaccesibles las conversaciones históricas de algunos usuarios, los usuarios de Voyager aún pudieron ver sus conversaciones guardadas en sus carpetas.</i>
</p>

---

## ✨ Funcionalidades

### 🌌 Núcleo Común (Gemini & AI Studio)

- **📂 [Organización por Carpetas](https://voyager.nagi.fun/es/guide/folders)**: Organiza tus chats en una jerarquía de dos niveles con soporte para **arrastrar y soltar** y **sincronización con Google Drive**.
  - **Gemini**: Soporta **Modo de Aislamiento de Cuenta** y **Colores de Carpeta Personalizados**.
- **💡 [Depósito de Prompts](https://voyager.nagi.fun/es/guide/prompts)**: Guarda y reutiliza tus mejores prompts en Gemini, AI Studio y [sitios web personalizados](https://voyager.nagi.fun/es/guide/custom-websites).
- **☁️ [Sincronización en la Nube](https://voyager.nagi.fun/es/guide/cloud-sync)**: Sincroniza tus carpetas y depósito de prompts con Google Drive.
- **📐 Copia de Fórmulas**: Copia en un clic los códigos fuente LaTeX y MathML (Word).
- **🌦️ Efectos Visuales**: Añade un ambiente estacional con **nieve**, **lluvia cinematográfica** o **pétalos de sakura** desde el panel de configuración.

### ✨ Funciones Exclusivas de Gemini

- **📍 [Navegación de Línea de Tiempo](https://voyager.nagi.fun/es/guide/timeline)**: Nodos visuales para saltar entre mensajes, destacar momentos clave y gestionar ramas de conversación.
- **💾 [Exportación de Chat](https://voyager.nagi.fun/es/guide/export)**: Exporta conversaciones a JSON, Markdown o PDF con imágenes incluidas.
- **🧜‍♀️ [Renderizado Mermaid](https://voyager.nagi.fun/es/guide/mermaid)**: Renderizado automático de diagramas de flujo, diagramas de secuencia y otros gráficos Mermaid.
- **📝 [Corrección de Renderizado Markdown](https://voyager.nagi.fun/es/guide/markdown-fix)**: Corrige automáticamente la sintaxis de negrita de Markdown dañada por los elementos HTML inyectados por Gemini.
- **🍌 [NanoBanana](https://voyager.nagi.fun/es/guide/nanobanana)**: Eliminación de marca de agua sin pérdida para imágenes generadas por Gemini.
- **🔬 [Deep Research](https://voyager.nagi.fun/es/guide/deep-research)**: Extrae procesos de pensamiento y enlaces de investigación de las sesiones de Deep Research.
- **🛠️ Herramientas de Productividad**:
  - **[Eliminación por Lote](https://voyager.nagi.fun/es/guide/batch-delete)**: Limpia tu historial de forma masiva.
  - **[Respuesta con Cita](https://voyager.nagi.fun/es/guide/quote-reply)**: Responde con contexto seleccionando texto.
  - **[Sincronización de Título de Pestaña](https://voyager.nagi.fun/es/guide/tab-title)**: Sincroniza automáticamente el título de la pestaña del navegador.
  - **[Evitar desplazamiento automático](https://voyager.nagi.fun/es/guide/prevent-auto-scroll)**: Intercepta el comportamiento de salto no deseado al enviar un mensaje.
  - **[Colapso de Entrada](https://voyager.nagi.fun/es/guide/input-collapse)**: Área de entrada auto-colapsable para más espacio de lectura.
  - **[Modelo Predeterminado](https://voyager.nagi.fun/es/guide/default-model)**: Establece tu modelo preferido por defecto.
  - **[Ocultar elementos recientes y Gems](https://voyager.nagi.fun/es/guide/recents-hider)**: Oculta la lista "Recientes" en la barra lateral para reducir las distracciones.

### 🎨 Personalización

- Abre el popup de la extensión y busca **Efectos Visuales** para cambiar entre `Apagado`, `Nieve`, `Sakura` y `Lluvia`.
- Los efectos se renderizan como capas ligeras a pantalla completa y no bloquean la interacción con la página.
- Al cambiar de efecto o desactivarlo, las partículas se desvanecen naturalmente en lugar de desaparecer abruptamente.

---

## 📥 Instalación

> ⚠️ Nota: El Administrador de Prompts es la única función que admite Gemini para Empresas.

<div align="center">
  <a href="https://chromewebstore.google.com/detail/kjdpnimcnfinmilocccippmododhceol?utm_source=github&utm_medium=readme&utm_campaign=organic_growth&utm_content=es" target="_blank">
    <img src="https://img.shields.io/badge/Chrome%20Web%20Store-4285F4?style=for-the-badge&logo=googlechrome&logoColor=white" alt="Chrome Web Store" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://microsoftedge.microsoft.com/addons/detail/gemini-voyager/gibmkggjijalcjinbdhcpklodjkhhlne" target="_blank">
    <img src="https://img.shields.io/badge/Microsoft%20Edge-0078D7?style=for-the-badge&logo=microsoftedge&logoColor=white" alt="Microsoft Edge Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://addons.mozilla.org/firefox/addon/gemini-voyager/" target="_blank">
    <img src="https://img.shields.io/badge/Firefox%20Add--ons-FF7139?style=for-the-badge&logo=firefox&logoColor=white" alt="Firefox Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://github.com/Nagi-ovo/gemini-voyager/releases/latest/" target="_blank">
    <img src="https://img.shields.io/badge/Safari-000000?style=for-the-badge&logo=safari&logoColor=white" alt="Safari Descargar" height="36">
  </a>
</div>

<p align="center">
  <sub><b>Chrome Web Store</b> también funciona en Edge, Opera, Brave, Vivaldi, Arc y otros navegadores Chromium.</sub>
</p>

> **Estado de la Tienda:** Chrome ✅ · Firefox ✅ · Edge ✅ · Safari ✅

Para **instalación manual** o **compilaciones de desarrollo**, consulta la [Guía de Instalación](https://voyager.nagi.fun/es/guide/installation).

---

## ☕ Apoya este Proyecto

<div align="center">
  <a href="https://github.com/Nagi-ovo/gemini-voyager">
    <img src="https://raw.githubusercontent.com/Nagi-ovo/gemini-voyager/main/docs/public/assets/sponsors.svg" width="1000px" />
  </a>
</div>

Si Voyager te facilita la vida, considera invitarme a un café. ¡Ayuda a mantener las actualizaciones! Los patrocinadores aparecerán en nuestra sección de Agradecimientos Especiales. ❤️

<div align="center">
  <a href="https://www.buymeacoffee.com/Nag1ovo" target="_blank">
    <img src="https://img.shields.io/badge/Invítame%20a%20un%20café-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" height="36">
  </a>
  <a href="https://github.com/sponsors/Nagi-ovo" target="_blank">
    <img src="https://img.shields.io/badge/Patrocíname-GitHub-ea4aaa?style=for-the-badge&logo=github&logoColor=white" alt="Sponsor on GitHub" height="36">
  </a>
  
  <p><b>O apoya a través de WeChat / Alipay / Afdian:</b></p>
  <table align="center" border="0" cellpadding="0" cellspacing="0">
    <tr>
      <td align="center">
        <img src="../docs/public/assets/wechat-sponsor.png" alt="WeChat Pay" height="160"><br>
        <sub><b>WeChat Pay</b></sub>
      </td>
      <td align="center">
        <img src="../docs/public/assets/alipay-sponsor.jpg" alt="Alipay" height="160"><br>
        <sub><b>Alipay</b></sub>
      </td>
      <td align="center">
        <a href="https://afdian.com/a/nagi-ovo" target="_blank">
          <picture>
            <source media="(prefers-color-scheme: dark)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo&bg_color=%230d1117&text_color=%23dedbd7&border_color=%232e343d" />
            <source media="(prefers-color-scheme: light)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" />
            <img alt="Nagi-ovo's Profile" src="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" height="160" />
          </picture>
        </a><br>
        <sub><b>Afdian</b></sub>
      </td>
    </tr>
  </table>
</div>

### 🎙️ Herramienta recomendada: Typeless

Recomiendo encarecidamente **[Typeless (typeless.com)](https://www.typeless.com/?via=gemini-voyager)**, una herramienta de voz a texto con IA que utilicé extensamente durante el desarrollo de Voyager. Integrarlo en mi flujo de trabajo diario me ha ahorrado muchísimo tiempo.

> 🎁 **[Únete a través de mi enlace de referido](https://www.typeless.com/?via=gemini-voyager)** (Código: _`gemini-voyager`_) para obtener **$5 de crédito gratis**. ❤️

---

## 🤝 Contribución y Desarrollo

[<img src="https://devin.ai/assets/askdeepwiki.png" alt="Ask DeepWiki" height="20"/>](https://deepwiki.com/Nagi-ovo/gemini-voyager)

¡Damos la bienvenida a las contribuciones!

- **Issues**: Usa nuestras plantillas de [informe de errores](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/bug_report.md) o [solicitud de funcionalidades](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/feature_request.yml).
- **Pull Requests**: Revisa [CONTRIBUTING.md](./CONTRIBUTING.md).

¡Gracias por ayudar a que Voyager sea mejor! ❤️

### ❤️ Agradecimientos Especiales

Un agradecimiento especial a todos los colaboradores por sus contribuciones a Voyager ❤️

<a href="https://github.com/Nagi-ovo/gemini-voyager/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=Nagi-ovo/gemini-voyager&max=200&columns=14" />
</a>

---

## 🌟 Créditos

- **[DeepSeek Voyager](https://github.com/Azurboy/deepseek-voyager)** - Un fork de Voyager adaptado para DeepSeek.

- **[claude-nexus](https://github.com/Qiuner/claude-nexus)** - Una extensión de mejora para Claude.ai inspirada en Voyager, con navegación de línea de tiempo, gestión de carpetas, biblioteca de prompts y más, con compatibilidad total de importación/exportación de prompts con Voyager.

- **[ChatGPT Conversation Timeline](https://github.com/Reborn14/chatgpt-conversation-timeline)** - La fuente original de inspiración para la navegación por línea de tiempo.

- **[Ophel Atlas](https://github.com/urzeye/ophel)** - Una extensión de navegador que transforma las conversaciones de IA en documentos organizados y buscables, con esquemas autogenerados, gestión de conversaciones y biblioteca de prompts, compatible con múltiples plataformas de IA.

---

<div align="center">
  <a href="https://www.star-history.com/#Nagi-ovo/gemini-voyager&type=date&legend=top-left">
   <picture>
     <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&theme=dark&legend=top-left" />
     <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
     <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
   </picture>
  </a>
  <p>Hecho con ❤️ por Jesse Zhang</p>
  <sub>GPLv3 License © 2026</sub>
</div>


================================================
FILE: .github/README_FR.md
================================================
<div align="center">
  <img src="../docs/public/assets/promotion/Promo-Banner.png" alt="promotion"/>
  <h3>Personnalisez votre expérience Gemini™ ✨</h3>
  <p>
    Navigation temporelle élégante, organisation des chats par dossiers, et coffre-fort de prompts personnel.<br>
    <b>C'est l'extension indispensable pour Google Gemini.</b>
  </p>
  
  <p>
    <img src="https://img.shields.io/badge/Chrome-✓-4285F4?style=flat-square&logo=googlechrome&logoColor=white" alt="Chrome">
    <img src="https://img.shields.io/badge/Edge-✓-0078D7?style=flat-square&logo=microsoftedge&logoColor=white" alt="Edge">
    <img src="https://img.shields.io/badge/Firefox-✓-FF7139?style=flat-square&logo=firefox&logoColor=white" alt="Firefox">
    <img src="https://img.shields.io/badge/Safari-✓-000000?style=flat-square&logo=safari&logoColor=white" alt="Safari">
    <img src="https://img.shields.io/badge/Opera-✓-FF1B2D?style=flat-square&logo=opera&logoColor=white" alt="Opera">
    <img src="https://img.shields.io/badge/Brave-✓-FB542B?style=flat-square&logo=brave&logoColor=white" alt="Brave">
  </p>
  <p>
    <img src="https://img.shields.io/github/stars/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub stars">
    <img src="https://img.shields.io/github/forks/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub forks">
    <img src="https://img.shields.io/github/v/release/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="Latest version">
    <img src="https://img.shields.io/github/downloads/Nagi-ovo/gemini-voyager/total?style=flat-square&logo=github" alt="GitHub downloads">
    <img src="https://img.shields.io/chrome-web-store/users/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome Web Store users">
    <img src="https://img.shields.io/chrome-web-store/rating/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome Web Store rating">
    <img src="https://img.shields.io/amo/users/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox Add-ons users">
    <img src="https://img.shields.io/amo/rating/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox Add-ons rating">
  </p>
  <p>
    <a href="https://trendshift.io/repositories/16094" target="_blank"><img src="https://trendshift.io/api/badge/repositories/16094" alt="Nagi-ovo%2Fgemini-voyager | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
    <a href="https://www.producthunt.com/products/gemini-voyager?embed=true&amp;utm_source=badge-featured&amp;utm_medium=badge&amp;utm_campaign=badge-gemini-voyager" target="_blank" rel="noopener noreferrer"><img alt="Gemini Voyager - All-in-one Gemini suite: folders, chat export and much more | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1064704&amp;theme=light&amp;t=1768842096186"></a>
  </p>
  <p align="center">
    ✨ Nous sommes en direct sur Product Hunt ! Vos retours sont les bienvenus. ❤️
  </p>
</div>

<p align="center">
  <a href="https://voyager.nagi.fun/fr">📖 Documentation</a> • 
  <a href="../README.md">English</a> • 
  <a href="./README_ZH.md">简体中文</a> •
  <a href="./README_ZH_TW.md">繁體中文</a> •
  <a href="./README_JA.md">日本語</a> •
  <a href="./README_ES.md">Español</a> •
  <a href="./README_PT.md">Português</a> •
  <a href="./README_RU.md">Русский</a> •
  <a href="./README_AR.md">العربية</a> •
  <a href="./README_KO.md">한국어</a>
</p>

<p align="center">
    <img src="https://count.getloli.com/@gemini-voyager?name=gemini-voyager&theme=rule34&padding=7&offset=0&align=top&scale=1&pixelated=1&darkmode=auto" width="400">
  </p>

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024507762483277927?s=20" target="_blank">
    <img src="../docs/public/assets/x-recommendation.png" alt="KOL Recommendation" width="500">
  </a>
  <br>
  <b>🎉 Fortement recommandé par les meilleurs influenceurs tech !</b>
</p>

> [!IMPORTANT]
> **Avis de changement de nom** : En raison de problèmes de marque et de droits d'auteur, cette extension a été officiellement renommée **Voyager**. Cependant, en raison de la lenteur extrême du processus de révision du Chrome Web Store, le changement de nom n'a pas été approuvé dans les 7 jours — elle est temporairement indisponible sur le Chrome Web Store.

> [!NOTE]
> Si Voyager vous est utile, partagez-le sur X, Reddit, YouTube, etc. Chaque partage aide plus de personnes à découvrir le projet et à améliorer l'expérience Gemini. Merci.

---

## 👋 Pourquoi Voyager ?

Nous adorons Gemini, mais nous aimerions parfois qu'il soit un peu plus "ordonné".

C'est pourquoi nous avons créé **Voyager**. Plus qu'un simple outil, c'est un compagnon qui vous aide à garder vos conversations IA organisées, accessibles et productives. Que vous soyez un chercheur gérant des dizaines de fils, un développeur sauvegardant des extraits de code, ou simplement quelqu'un qui aime l'ordre, Voyager est fait pour vous.

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024509398601597412?s=20" target="_blank">
    <img src="../docs/public/assets/try-voyager.png" alt="Try Voyager" width="500">
  </a>
  <br>
  <i>Lors du problème du 18 février où l'application Google Gemini a rendu inaccessibles les conversations historiques de certains utilisateurs, les utilisateurs de Voyager ont toujours pu voir leurs conversations enregistrées dans leurs dossiers.</i>
</p>

---

## ✨ Fonctionnalités

### 🌌 Noyau Commun (Gemini & AI Studio)

- **📂 [Dossiers](https://voyager.nagi.fun/fr/guide/folders)** : Organisez vos chats dans une hiérarchie à deux niveaux avec support du **glisser-déposer** et **synchronisation Google Drive**.
  - **Gemini** : Support du **mode d'isolation de compte** et **couleurs de dossiers personnalisées**.
- **💡 [Coffre-fort de Prompts](https://voyager.nagi.fun/fr/guide/prompts)** : Enregistrez et réutilisez vos meilleurs prompts sur Gemini, AI Studio et [sites personnalisés](https://voyager.nagi.fun/fr/guide/custom-websites).
- **☁️ [Sincronisation Cloud](https://voyager.nagi.fun/fr/guide/cloud-sync)** : Synchronisez vos dossiers et coffre-fort de prompts avec Google Drive.
- **📐 Copie de Formules**: Copie en un clic des codes sources LaTeX et MathML (Word).
- **🌦️ Effets Visuels** : Ajoutez une ambiance saisonnière avec **neige**, **pluie cinématique** ou **pétales de sakura** depuis le panneau de paramètres.

### ✨ Fonctions Exclusives Gemini

- **📍 [Navigation Temporelle](https://voyager.nagi.fun/fr/guide/timeline)** : Des nœuds visuels pour naviguer entre les messages, marquer les moments clés et gérer les branches de conversation.
- **💾 [Export de Chat](https://voyager.nagi.fun/fr/guide/export)** : Exportez vos conversations en JSON, Markdown ou PDF avec les images incluses.
- **🧜‍♀️ [Rendu Mermaid](https://voyager.nagi.fun/fr/guide/mermaid)**: Rendu automatique des organigrammes, diagrammes de séquence et autres graphiques Mermaid.
- **📝 [Correction du Rendu Markdown](https://voyager.nagi.fun/fr/guide/markdown-fix)**: Répare automatiquement la syntaxe Markdown grasse corrompue par les éléments HTML injectés par Gemini.
- **🍌 [NanoBanana](https://voyager.nagi.fun/fr/guide/nanobanana)** : Suppression sans perte du filigrane Gemini sur les images générées.
- **🔬 [Deep Research](https://voyager.nagi.fun/fr/guide/deep-research)** : Extrayez les processus de réflexion et les liens de recherche des sessions Deep Research.
- **🛠️ Outils de Productivité** :
  - **[Suppression par Lot](https://voyager.nagi.fun/fr/guide/batch-delete)** : Nettoyage massif de votre historique.
  - **[Réponse avec Citation](https://voyager.nagi.fun/fr/guide/quote-reply)** : Répondez avec contexte en sélectionnant simplement du texte.
  - **[Synchro Titre Onglet](https://voyager.nagi.fun/fr/guide/tab-title)** : Synchronisation automatique du titre de l'onglet avec le titre du chat.
  - **[Empêcher le défilement auto](https://voyager.nagi.fun/fr/guide/prevent-auto-scroll)** : Intercepte le saut incontrôlé vers le bas de la page.
  - **[Réduction Entrée](https://voyager.nagi.fun/fr/guide/input-collapse)** : Zone de saisie auto-réductible pour plus d'espace de lecture.
  - **[Modèle par Défaut](https://voyager.nagi.fun/fr/guide/default-model)** : Définissez votre modèle préféré par défaut.
  - **[Masquer les éléments récents et Gems](https://voyager.nagi.fun/fr/guide/recents-hider)** : Masquer la liste "Récents" dans la barre latérale pour réduire les distractions.

### 🎨 Personnalisation

- Ouvrez le popup de l'extension et recherchez **Effets Visuels** pour basculer entre `Désactivé`, `Neige`, `Sakura` et `Pluie`.
- Les effets sont rendus sous forme de couches légères plein écran qui ne bloquent pas l'interaction avec la page.
- Lors du changement d'effet ou de la désactivation, les particules s'estompent naturellement au lieu de disparaître brusquement.

---

## 📥 Installation

> ⚠️ Note : Le Gestionnaire de Prompts est la seule fonctionnalité supportant Gemini for Enterprise.

<div align="center">
  <a href="https://chromewebstore.google.com/detail/kjdpnimcnfinmilocccippmododhceol?utm_source=github&utm_medium=readme&utm_campaign=organic_growth&utm_content=fr" target="_blank">
    <img src="https://img.shields.io/badge/Chrome%20Web%20Store-4285F4?style=for-the-badge&logo=googlechrome&logoColor=white" alt="Chrome Web Store" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://microsoftedge.microsoft.com/addons/detail/gemini-voyager/gibmkggjijalcjinbdhcpklodjkhhlne" target="_blank">
    <img src="https://img.shields.io/badge/Microsoft%20Edge-0078D7?style=for-the-badge&logo=microsoftedge&logoColor=white" alt="Microsoft Edge Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://addons.mozilla.org/firefox/addon/gemini-voyager/" target="_blank">
    <img src="https://img.shields.io/badge/Firefox%20Add--ons-FF7139?style=for-the-badge&logo=firefox&logoColor=white" alt="Firefox Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://github.com/Nagi-ovo/gemini-voyager/releases/latest/" target="_blank">
    <img src="https://img.shields.io/badge/Safari-000000?style=for-the-badge&logo=safari&logoColor=white" alt="Safari Télécharger" height="36">
  </a>
</div>

<p align="center">
  <sub>Le <b>Chrome Web Store</b> fonctionne aussi sur Edge, Opera, Brave, Vivaldi, Arc et autres navigateurs Chromium.</sub>
</p>

> **Statut des Stores :** Chrome ✅ · Firefox ✅ · Edge ✅ · Safari ✅

Pour une **installation manuelle** ou des **builds de développement**, veuillez vous référer au [Guide d'Installation](https://voyager.nagi.fun/fr/guide/installation).

---

## ☕ Soutenir le Projet

<div align="center">
  <a href="https://github.com/Nagi-ovo/gemini-voyager">
    <img src="https://raw.githubusercontent.com/Nagi-ovo/gemini-voyager/main/docs/public/assets/sponsors.svg" width="1000px" />
  </a>
</div>

Si Voyager facilite votre vie, considérez m'offrir un café. Cela aide à maintenir les mises à jour ! Les donateurs seront cités dans notre section remerciements. ❤️

<div align="center">
  <a href="https://www.buymeacoffee.com/Nag1ovo" target="_blank">
    <img src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" height="36">
  </a>
  <a href="https://github.com/sponsors/Nagi-ovo" target="_blank">
    <img src="https://img.shields.io/badge/Sponsor%20me-GitHub-ea4aaa?style=for-the-badge&logo=github&logoColor=white" alt="Sponsor on GitHub" height="36">
  </a>
  
  <p><b>Ou via WeChat / Alipay / Afdian :</b></p>
  <table align="center" border="0" cellpadding="0" cellspacing="0">
    <tr>
      <td align="center">
        <img src="../docs/public/assets/wechat-sponsor.png" alt="WeChat Pay" height="160"><br>
        <sub><b>WeChat Pay</b></sub>
      </td>
      <td align="center">
        <img src="../docs/public/assets/alipay-sponsor.jpg" alt="Alipay" height="160"><br>
        <sub><b>Alipay</b></sub>
      </td>
      <td align="center">
        <a href="https://afdian.com/a/nagi-ovo" target="_blank">
          <picture>
            <source media="(prefers-color-scheme: dark)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo&bg_color=%230d1117&text_color=%23dedbd7&border_color=%232e343d" />
            <source media="(prefers-color-scheme: light)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" />
            <img alt="Nagi-ovo's Profile" src="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" height="160" />
          </picture>
        </a><br>
        <sub><b>Afdian</b></sub>
      </td>
    </tr>
  </table>
</div>

### 🎙️ Outil recommandé : Typeless

Je recommande vivement **[Typeless (typeless.com)](https://www.typeless.com/?via=gemini-voyager)**, un outil de synthèse vocale IA que j'ai utilisé intensivement durant le développement. Il booste considérablement la productivité.

> 🎁 **[Rejoignez via mon lien](https://www.typeless.com/?via=gemini-voyager)** (Code : _`gemini-voyager`_) pour obtenir **5$ de crédits gratuits**. ❤️

---

## 🤝 Contribution & Développement

[<img src="https://devin.ai/assets/askdeepwiki.png" alt="Ask DeepWiki" height="20"/>](https://deepwiki.com/Nagi-ovo/gemini-voyager)

Toute contribution est la bienvenue !

- **Issues** : Utilisez nos templates de [bug report](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/bug_report.md) ou [feature request](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/feature_request.yml).
- **Pull Requests** : Consultez [CONTRIBUTING.md](./CONTRIBUTING.md).

Merci d'aider à rendre Voyager meilleur ! ❤️

### ❤️ Remerciements Spéciaux

Un grand merci à tous les contributeurs pour leurs contributions à Voyager ❤️

<a href="https://github.com/Nagi-ovo/gemini-voyager/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=Nagi-ovo/gemini-voyager&max=200&columns=14" />
</a>

---

## 🌟 Crédits

- **[DeepSeek Voyager](https://github.com/Azurboy/deepseek-voyager)** - Un fork de Voyager adapté pour DeepSeek.

- **[claude-nexus](https://github.com/Qiuner/claude-nexus)** - Une extension d’amélioration pour Claude.ai inspirée de Voyager, avec navigation par timeline, gestion de dossiers, bibliothèque de prompts, et une compatibilité totale d’import/export des prompts avec Voyager !

- **[ChatGPT Conversation Timeline](https://github.com/Reborn14/chatgpt-conversation-timeline)** - La source d'inspiration originale pour la navigation temporelle.

- **[Ophel Atlas](https://github.com/urzeye/ophel)** - Une extension de navigateur qui transforme les conversations IA en documents organisés et consultables, avec génération automatique de plans, gestion des conversations et bibliothèque de prompts, compatible avec plusieurs plateformes d'IA.

---

<div align="center">
  <a href="https://www.star-history.com/#Nagi-ovo/gemini-voyager&type=date&legend=top-left">
   <picture>
     <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&theme=dark&legend=top-left" />
     <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
     <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
   </picture>
  </a>
  <p>Fait avec ❤️ par Jesse Zhang</p>
  <sub>GPLv3 License © 2026</sub>
</div>


================================================
FILE: .github/README_JA.md
================================================
<div align="center">
  <img src="../docs/public/assets/promotion/Promo-Banner-jp.png" alt="promotion"/>
  <h3>Gemini™ 体験を自分好みに ✨</h3>
  <p>
    エレガントなタイムライン、フォルダ管理、そしてプロンプト管理。<br>
    <b>Gemini に足りなかった「最後のピース」がここにあります。</b>
  </p>
  
  <p>
    <img src="https://img.shields.io/badge/Chrome-✓-4285F4?style=flat-square&logo=googlechrome&logoColor=white" alt="Chrome">
    <img src="https://img.shields.io/badge/Edge-✓-0078D7?style=flat-square&logo=microsoftedge&logoColor=white" alt="Edge">
    <img src="https://img.shields.io/badge/Firefox-✓-FF7139?style=flat-square&logo=firefox&logoColor=white" alt="Firefox">
    <img src="https://img.shields.io/badge/Safari-✓-000000?style=flat-square&logo=safari&logoColor=white" alt="Safari">
    <img src="https://img.shields.io/badge/Opera-✓-FF1B2D?style=flat-square&logo=opera&logoColor=white" alt="Opera">
    <img src="https://img.shields.io/badge/Brave-✓-FB542B?style=flat-square&logo=brave&logoColor=white" alt="Brave">
  </p>
  <p>
    <img src="https://img.shields.io/github/stars/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub Star">
    <img src="https://img.shields.io/github/forks/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub Fork">
    <img src="https://img.shields.io/github/v/release/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="最新バージョン">
    <img src="https://img.shields.io/github/downloads/Nagi-ovo/gemini-voyager/total?style=flat-square&logo=github" alt="GitHub ダウンロード数">
    <img src="https://img.shields.io/chrome-web-store/users/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome ユーザー数">
    <img src="https://img.shields.io/chrome-web-store/rating/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome 評価">
    <img src="https://img.shields.io/amo/users/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox ユーザー数">
    <img src="https://img.shields.io/amo/rating/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox 評価">
  </p>
  <p>
    <a href="https://trendshift.io/repositories/16094" target="_blank"><img src="https://trendshift.io/api/badge/repositories/16094" alt="Nagi-ovo%2Fgemini-voyager | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
    <a href="https://www.producthunt.com/products/gemini-voyager?embed=true&amp;utm_source=badge-featured&amp;utm_medium=badge&amp;utm_campaign=badge-gemini-voyager" target="_blank" rel="noopener noreferrer"><img alt="Gemini Voyager - All-in-one Gemini suite: folders, chat export and much more | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1064704&amp;theme=light&amp;t=1768842096186"></a>
  </p>
  <p align="center">
    ✨ Product Hunt に登場しました!応援よろしくお願いします。❤️
  </p>
</div>

<p align="center">
  <a href="https://voyager.nagi.fun/ja">📖 ドキュメント</a> • 
  <a href="../README.md">English</a> • 
  <a href="./README_ZH.md">简体中文</a> •
  <a href="./README_ZH_TW.md">繁體中文</a> •
  <a href="./README_FR.md">Français</a> •
  <a href="./README_ES.md">Español</a> •
  <a href="./README_PT.md">Português</a> •
  <a href="./README_RU.md">Русский</a> •
  <a href="./README_AR.md">العربية</a> •
  <a href="./README_KO.md">한국어</a>
</p>

<p align="center">
    <img src="https://count.getloli.com/@gemini-voyager?name=gemini-voyager&theme=rule34&padding=7&offset=0&align=top&scale=1&pixelated=1&darkmode=auto" width="400">
  </p>

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024507762483277927?s=20" target="_blank">
    <img src="../docs/public/assets/x-recommendation.png" alt="KOL Recommendation" width="500">
  </a>
  <br>
  <b>🎉 トップテック KOL やコミュニティから強く推奨されています!</b>
</p>

> [!IMPORTANT]
> **名称変更のお知らせ**:商標・著作権上の問題により、本拡張機能は正式に **Voyager** へ改名されました。ただし、Chrome ウェブストアの審査が非常に遅いため、7 日以内に名称変更が承認されず、現在 Chrome Web Store では一時的にご利用いただけない状態です。

> [!NOTE]
> もし Voyager が役に立っているなら、X、YouTube、Reddit などで共有してもらえると嬉しいです。シェアが増えるほど、このプロジェクトをより多くの人に届けられ、Gemini の体験改善にもつながります。ありがとう。

---

## 👋 Voyager とは?

私たちは Gemini が大好きですが、時にはもう少し「秩序」が欲しいと感じることがあります。

それが **Voyager** を開発した理由です。これは単なるツールではなく、AI との会話を整理し、アクセスしやすく、生産的にするためのパートナーです。多くのスレッドを扱う研究者、コードを保存したい開発者、あるいは単に整理整頓が好きな方、Voyager はあなたのための拡張機能です。

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024509398601597412?s=20" target="_blank">
    <img src="../docs/public/assets/try-voyager.png" alt="Try Voyager" width="500">
  </a>
  <br>
  <i>2 月 18 日に Google Gemini App が一部のユーザーの履歴会話にアクセスできなくなる問題を引き起こした際、Voyager のユーザーは引き続きフォルダ内に保存された会話を見ることができました。</i>
</p>

---

## ✨ 主な機能

### 🌌 共通コア (Gemini & AI Studio)

- **📂 [フォルダ管理](https://voyager.nagi.fun/ja/guide/folders)**: **階層構造**、**ドラッグ&ドロップによる並べ替え**、**Google ドライブ同期**をサポート。
  - **Gemini**: **マルチアカウント分離モード**と**カスタムフォルダカラー**をサポート。
- **💡 [プロンプト管理](https://voyager.nagi.fun/ja/guide/prompts)**: プロンプトを保存して再利用。Gemini、AI Studio、[カスタムサイト](https://voyager.nagi.fun/ja/guide/custom-websites)で使用可能。
- **☁️ [クラウド同期](https://voyager.nagi.fun/ja/guide/cloud-sync)**: フォルダとプロンプトを Google ドライブに同期します。
- **📐 数式コピー**: LaTeX および MathML (Word) のソースコードを一クリックでコピー。
- **🌦️ ビジュアルエフェクト**: **雪**、**映画的な雨**、**桜の花びら**で季節の雰囲気を演出。設定パネルから切り替え可能。

### ✨ Gemini 専用機能

- **📍 [タイムライン](https://voyager.nagi.fun/ja/guide/timeline)**: 会話構造を可視化し、メッセージ間を瞬時に移動。重要なメッセージのスター保存も可能。
- **💾 [エクスポート](https://voyager.nagi.fun/ja/guide/export)**: 会話を JSON、Markdown、PDF 形式で保存(画像込み)。
- **🧜‍♀️ [Mermaid レンダリング](https://voyager.nagi.fun/ja/guide/mermaid)**: フローチャート、シーケンス図、その他の Mermaid チャートを自動レンダリングします。
- **📝 [Markdown レンダリングの修正](https://voyager.nagi.fun/ja/guide/markdown-fix)**: Gemini が挿入した HTML 要素によって壊れた Markdown の太字構文を自動的に修正します。
- **🍌 [NanoBanana](https://voyager.nagi.fun/ja/guide/nanobanana)**: Gemini で生成された画像からウォーターマークを自動除去。
- **🔬 [Deep Research](https://voyager.nagi.fun/ja/guide/deep-research)**: 思考プロセスやリサーチリンクを Markdown で抽出。
- **🛠️ パワーツール**:
  - **[一括削除](https://voyager.nagi.fun/ja/guide/batch-delete)**: 履歴をまとめてクリーンアップ。
  - **[引用返信](https://voyager.nagi.fun/ja/guide/quote-reply)**: テキストを選択してワンクリックで引用。
  - **[タブタイトルの同期](https://voyager.nagi.fun/ja/guide/tab-title)**: タブの名前をチャットのタイトルに自動変更。
  - **[自動スクロール防止](https://voyager.nagi.fun/ja/guide/prevent-auto-scroll)**: 新しいプロンプトを送信する際の迷惑なジャンプ動作を阻止します。
  - **[入力欄の自動非表示](https://voyager.nagi.fun/ja/guide/input-collapse)**: 未入力時に折りたたんで表示スペースを確保。
  - **[デフォルトモデル](https://voyager.nagi.fun/ja/guide/default-model)**: 新しい対話に使用するデフォルトのモデルを設定します。
  - **[最近の項目と Gem を非表示](https://voyager.nagi.fun/ja/guide/recents-hider)**: サイドバーの「最近」リストを非表示にして、集中力を高めます。

### 🎨 パーソナライズ

- 拡張機能のポップアップを開き、**ビジュアルエフェクト** で `オフ`、`雪`、`桜`、`雨` を切り替えられます。
- エフェクトは軽量なフルスクリーンオーバーレイとしてレンダリングされ、ページの操作を妨げません。
- エフェクトを切り替えたりオフにしたりすると、パーティクルは突然消えるのではなく、自然にフェードアウトします。

---

## 📥 インストール

> ⚠️ 注意:プロンプト管理のみが Gemini for Enterprise をサポートしています。

<div align="center">
  <a href="https://chromewebstore.google.com/detail/kjdpnimcnfinmilocccippmododhceol?utm_source=github&utm_medium=readme&utm_campaign=organic_growth&utm_content=ja" target="_blank">
    <img src="https://img.shields.io/badge/Chrome%20ストア-4285F4?style=for-the-badge&logo=googlechrome&logoColor=white" alt="Chrome Web Store" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://microsoftedge.microsoft.com/addons/detail/gemini-voyager/gibmkggjijalcjinbdhcpklodjkhhlne" target="_blank">
    <img src="https://img.shields.io/badge/Microsoft%20Edge-0078D7?style=for-the-badge&logo=microsoftedge&logoColor=white" alt="Microsoft Edge Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://addons.mozilla.org/firefox/addon/gemini-voyager/" target="_blank">
    <img src="https://img.shields.io/badge/Firefox%20アドオン-FF7139?style=for-the-badge&logo=firefox&logoColor=white" alt="Firefox Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://github.com/Nagi-ovo/gemini-voyager/releases/latest/" target="_blank">
    <img src="https://img.shields.io/badge/Safari-000000?style=for-the-badge&logo=safari&logoColor=white" alt="Safari ダウンロード" height="36">
  </a>
</div>

<p align="center">
  <sub><b>Chrome ストア版</b>は Edge, Opera, Brave, Vivaldi, Arc などの Chromium 系ブラウザでも動作します。</sub>
</p>

> **ストア状況:** Chrome ✅ · Firefox ✅ · Edge ✅ · Safari ✅

**手動インストール**や**開発用ビルド**については、[インストールガイド](https://voyager.nagi.fun/ja/guide/installation)を参照してください。

---

## ☕ 支援のお願い

<div align="center">
  <a href="https://github.com/Nagi-ovo/gemini-voyager">
    <img src="https://raw.githubusercontent.com/Nagi-ovo/gemini-voyager/main/docs/public/assets/sponsors.svg" width="1000px" />
  </a>
</div>

Voyager がお役に立ちましたら、コーヒー一杯分のご支援をいただけますと幸いです。アップデートの継続に繋がります!❤️

<div align="center">
  <a href="https://www.buymeacoffee.com/Nag1ovo" target="_blank">
    <img src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" height="36">
  </a>
  <a href="https://github.com/sponsors/Nagi-ovo" target="_blank">
    <img src="https://img.shields.io/badge/Sponsor%20me-GitHub-ea4aaa?style=for-the-badge&logo=github&logoColor=white" alt="Sponsor on GitHub" height="36">
  </a>
  
  <p><b>WeChat / Alipay / Afdian でも支援可能です:</b></p>
  <table align="center" border="0" cellpadding="0" cellspacing="0">
    <tr>
      <td align="center">
        <img src="../docs/public/assets/wechat-sponsor.png" alt="WeChat Pay" height="160"><br>
        <sub><b>WeChat Pay</b></sub>
      </td>
      <td align="center">
        <img src="../docs/public/assets/alipay-sponsor.jpg" alt="Alipay" height="160"><br>
        <sub><b>Alipay</b></sub>
      </td>
      <td align="center">
        <a href="https://afdian.com/a/nagi-ovo" target="_blank">
          <picture>
            <source media="(prefers-color-scheme: dark)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo&bg_color=%230d1117&text_color=%23dedbd7&border_color=%232e343d" />
            <source media="(prefers-color-scheme: light)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" />
            <img alt="Nagi-ovo's Profile" src="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" height="160" />
          </picture>
        </a><br>
        <sub><b>Afdian</b></sub>
      </td>
    </tr>
  </table>
</div>

### 🎙️ おすすめツール:Typeless

Voyager の開発中、私は AI 音声入力ツール **[Typeless (typeless.com)](https://www.typeless.com/?via=gemini-voyager)** を愛用していました。日々のワークフローに欠かせない、生産性を劇的に向上させてくれるツールです。

> 🎁 **[こちらのリンク](https://www.typeless.com/?via=gemini-voyager)**(招待コード:_`gemini-voyager`_)から登録すると、**5 ドルの無料クレジット**がもらえます。❤️

---

## 🤝 貢献と開発

[<img src="https://devin.ai/assets/askdeepwiki.png" alt="Ask DeepWiki" height="20"/>](https://deepwiki.com/Nagi-ovo/gemini-voyager)

バグ報告、機能提案、ドキュメントの改善など、あらゆる貢献を歓迎します!

- **Issues**: [バグ報告](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/bug_report.md) または [機能提案](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/feature_request.yml) テンプレートを使用してください。
- **Pull Requests**: [CONTRIBUTING.md](./CONTRIBUTING.md) をご確認ください。

<details>
<summary>開発環境の構築</summary>

```bash
# 依存関係のインストール (Bun 推奨)
bun i

# 開発モード (ホットリロード対応)
bun run dev:chrome
bun run dev:firefox
bun run dev:safari

# ビルド
bun run build:all
```

**Safari での開発**: 詳細は [safari/README.md](../safari/README.md) を参照してください。

</details>

Voyager をより良くするために協力してくださり、ありがとうございます!❤️

### ❤️ スペシャルサンクス

Voyager に貢献してくださったすべてのコントリビューターに感謝します ❤️

<a href="https://github.com/Nagi-ovo/gemini-voyager/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=Nagi-ovo/gemini-voyager&max=200&columns=14" />
</a>

---

## 🌟 クレジット

- **[DeepSeek Voyager](https://github.com/Azurboy/deepseek-voyager)** - Voyager を DeepSeek 向けに移植したプロジェクト。

- **[claude-nexus](https://github.com/Qiuner/claude-nexus)** - Voyager に着想を得た Claude.ai 向け拡張機能。タイムラインナビゲーション、フォルダ管理、プロンプトライブラリなどを備え、Voyager のプロンプトのインポート/エクスポートと完全互換です!

- **[ChatGPT Conversation Timeline](https://github.com/Reborn14/chatgpt-conversation-timeline)** - オリジナルの ChatGPT 向け拡張機能。このプロジェクトのインスピレーションの源です。

- **[Ophel Atlas](https://github.com/urzeye/ophel)** - AI チャットを整理された検索可能なドキュメントに変換するブラウザ拡張機能。アウトラインの自動生成、会話管理、プロンプトライブラリを備え、複数の AI プラットフォームに対応しています。

---

<div align="center">
  <a href="https://www.star-history.com/#Nagi-ovo/gemini-voyager&type=date&legend=top-left">
   <picture>
     <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&theme=dark&legend=top-left" />
     <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
     <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
   </picture>
  </a>
  <p>Made with ❤️ by Jesse Zhang</p>
  <sub>GPLv3 License © 2026</sub>
</div>


================================================
FILE: .github/README_KO.md
================================================
<div align="center">
  <img src="../docs/public/assets/promotion/Promo-Banner-KO.png" alt="promotion"/>
  <h3>Gemini™ 경험을 진정으로 당신의 것으로 만드세요 ✨</h3>
  <p>
    우아한 타임라인으로 대화를 탐색하고, 폴더로 채팅을 정리하며, 자신만의 프롬프트 저장소를 구축하세요.<br>
    <b>Google Gemini 를 위한 필수 강화 도구입니다.</b>
  </p>
  
  <p>
    <img src="https://img.shields.io/badge/Chrome-✓-4285F4?style=flat-square&logo=googlechrome&logoColor=white" alt="Chrome">
    <img src="https://img.shields.io/badge/Edge-✓-0078D7?style=flat-square&logo=microsoftedge&logoColor=white" alt="Edge">
    <img src="https://img.shields.io/badge/Firefox-✓-FF7139?style=flat-square&logo=firefox&logoColor=white" alt="Firefox">
    <img src="https://img.shields.io/badge/Safari-✓-000000?style=flat-square&logo=safari&logoColor=white" alt="Safari">
    <img src="https://img.shields.io/badge/Opera-✓-FF1B2D?style=flat-square&logo=opera&logoColor=white" alt="Opera">
    <img src="https://img.shields.io/badge/Brave-✓-FB542B?style=flat-square&logo=brave&logoColor=white" alt="Brave">
  </p>
  <p>
    <img src="https://img.shields.io/github/stars/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub stars">
    <img src="https://img.shields.io/github/forks/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub forks">
    <img src="https://img.shields.io/github/v/release/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="Latest version">
    <img src="https://img.shields.io/github/downloads/Nagi-ovo/gemini-voyager/total?style=flat-square&logo=github" alt="GitHub downloads">
    <img src="https://img.shields.io/chrome-web-store/users/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome Web Store users">
    <img src="https://img.shields.io/chrome-web-store/rating/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome Web Store rating">
    <img src="https://img.shields.io/amo/users/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox Add-ons users">
    <img src="https://img.shields.io/amo/rating/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox Add-ons rating">
  </p>
  <p>
    <a href="https://trendshift.io/repositories/16094" target="_blank"><img src="https://trendshift.io/api/badge/repositories/16094" alt="Nagi-ovo%2Fgemini-voyager | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
    <a href="https://www.producthunt.com/products/gemini-voyager?embed=true&amp;utm_source=badge-featured&amp;utm_medium=badge&amp;utm_campaign=badge-gemini-voyager" target="_blank" rel="noopener noreferrer"><img alt="Gemini Voyager - All-in-one Gemini suite: folders, chat export and much more | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1064704&amp;theme=light&amp;t=1768842096186"></a>
  </p>
  <p align="center">
    ✨ Product Hunt 에 출시되었습니다! 여러분의 의견과 피드백을 환영합니다. ❤️
  </p>
</div>

<p align="center">
  <a href="https://voyager.nagi.fun/ko">📖 문서</a> • 
  <a href="../README.md">English</a> •
  <a href="./README_ZH.md">简体中文</a> •
  <a href="./README_ZH_TW.md">繁體中文</a> •
  <a href="./README_JA.md">日本語</a> •
  <a href="./README_FR.md">Français</a> •
  <a href="./README_ES.md">Español</a> •
  <a href="./README_PT.md">Português</a> •
  <a href="./README_RU.md">Русский</a> •
  <a href="./README_AR.md">العربية</a>
</p>

<p align="center">
    <img src="https://count.getloli.com/@gemini-voyager?name=gemini-voyager&theme=rule34&padding=7&offset=0&align=top&scale=1&pixelated=1&darkmode=auto" width="400">
  </p>

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024507762483277927?s=20" target="_blank">
    <img src="../docs/public/assets/x-recommendation.png" alt="KOL Recommendation" width="500">
  </a>
  <br>
  <b>🎉 주요 기술 KOL 및 커뮤니티에서 적극 권장합니다!</b>
</p>

> [!IMPORTANT]
> **이름 변경 안내**: 상표 및 저작권 문제로 인해 이 확장 프로그램이 공식적으로 **Voyager**로 이름이 변경되었습니다. 하지만 Chrome 웹 스토어의 심사 속도가 매우 느려 7일 이내에 이름 변경이 승인되지 않아, 현재 Chrome Web Store에서 일시적으로 이용할 수 없는 상태입니다.

> [!NOTE]
> Voyager 가 도움이 되었다면 X, YouTube, Reddit 등에서 공유해 주세요. 공유가 늘수록 더 많은 사용자가 프로젝트를 발견하고 Gemini 사용 경험도 함께 좋아집니다. 감사합니다.

---

## 👋 왜 Voyager 인가요?

우리는 Gemini 를 사랑하지만, 때로는 조금 더 구조화된 정리가 필요하다고 느꼈습니다.

그래서 우리는 **Voyager**를 만들었습니다. 이것은 단순한 도구가 아니라, AI 대화를 정리하고 액세스 가능하며 생산적으로 유지하도록 돕는 동반자입니다. 수십 개의 스레드를 관리하는 연구자이든, 코드 스니펫을 저장하는 개발자이든, 단순히 정리를 좋아하는 사람이든 Voyager 는 당신을 위해 설계되었습니다.

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024509398601597412?s=20" target="_blank">
    <img src="../docs/public/assets/try-voyager.png" alt="Try Voyager" width="500">
  </a>
  <br>
  <i>2 월 18 일 Google Gemini 앱으로 인해 일부 사용자의 과거 대화에 접근할 수 없는 문제가 발생했을 때, Voyager 사용자는 여전히 폴더에 저장된 대화를 볼 수 있었습니다.</i>
</p>

---

## ✨ 주요 기능

### 🌌 공통 (Gemini & AI Studio)

- **📂 [폴더 관리](https://voyager.nagi.fun/ko/guide/folders)**: 드래그 앤 드롭을 지원하는 2 단계 폴더 계층 구조로 대화를 정리하세요.
  - **Gemini**: **계정 분리 모드** 및 **사용자 지정 폴더 색상**을 지원합니다.
- **💡 [프롬프트 저장소](https://voyager.nagi.fun/ko/guide/prompts)**: Gemini, AI Studio 및 [사용자 지정 웹사이트](https://voyager.nagi.fun/ko/guide/custom-websites)에서 프롬프트를 저장하고 재사용하세요.
- **☁️ [클라우드 동기화](https://voyager.nagi.fun/ko/guide/cloud-sync)**: 폴더와 프롬프트를 Google Drive 에 동기화하세요.
- **📐 수식 복사**: LaTeX 및 MathML (Word) 소스 코드를 클릭 한 번으로 복사하세요.
- **🌦️ 시각 효과**: 설정 패널에서 **눈**, **시네마틱 비**, **벚꽃잎** 효과를 전환하여 계절 분위기를 연출하세요.

### ✨ Gemini 전용 기능

- **📍 [타임라인 탐색](https://voyager.nagi.fun/ko/guide/timeline)**: 시각적 노드를 통해 메시지 간 이동, 주요 순간 별표 표시, 대화 브랜치 관리를 수행하세요.
- **💾 [대화 내보내기](https://voyager.nagi.fun/ko/guide/export)**: 이미지를 포함하여 JSON, Markdown 또는 PDF 로 대화를 내보내세요.
- **🧜‍♀️ [Mermaid 렌더링](https://voyager.nagi.fun/ko/guide/mermaid)**: 플로우차트, 시퀀스 다이어그램 및 기타 Mermaid 차트를 자동으로 렌더링합니다.
- **📝 [Markdown 렌더링 수정](https://voyager.nagi.fun/ko/guide/markdown-fix)**: Gemini 가 삽입한 HTML 요소로 인해 깨진 굵게 표시 (bold) 구문을 자동으로 수정합니다.
- **🍌 [NanoBanana](https://voyager.nagi.fun/ko/guide/nanobanana)**: Gemini 에서 생성된 이미지의 워터마크를 무손실로 제거합니다.
- **🔬 [Deep Research](https://voyager.nagi.fun/ko/guide/deep-research)**: Deep Research 세션에서 생각 과정과 연구 링크를 추출합니다.
- **🛠️ 파워 툴**:
  - **[일괄 삭제](https://voyager.nagi.fun/ko/guide/batch-delete)**: 여러 대화를 한꺼번에 삭제합니다.
  - **[인용 답장](https://voyager.nagi.fun/ko/guide/quote-reply)**: 텍스트를 선택하여 문맥과 함께 답장합니다.
  - **[탭 제목 동기화](https://voyager.nagi.fun/ko/guide/tab-title)**: 브라우저 탭 제목을 자동으로 동기화합니다.
  - **[자동 스크롤 방지](https://voyager.nagi.fun/ko/guide/prevent-auto-scroll)**: 새로운 프롬프트를 전송할 때 발생하는 원치 않는 점프 동작을 차단합니다.
  - **[입력창 접기](https://voyager.nagi.fun/ko/guide/input-collapse)**: 더 많은 읽기 공간을 위해 자동으로 확장되는 입력 영역을 제공합니다.
  - **[기본 모델](https://voyager.nagi.fun/ko/guide/default-model)**: 좋아하는 모델을 기본값으로 설정하세요.
  - **[최근 항목 숨기기](https://voyager.nagi.fun/ko/guide/recents-hider)**: 사이드바에서 "최근" 목록을 숨겨 산만함을 줄입니다.

### 🎨 개인화

- 확장 프로그램 팝업을 열고 **시각 효과**에서 `끄기`, `눈`, `벚꽃`, `비`를 전환할 수 있습니다.
- 효과는 가벼운 전체 화면 오버레이로 렌더링되며, 페이지 상호작용을 차단하지 않습니다.
- 효과를 전환하거나 끄면 파티클이 갑자기 사라지는 대신 자연스럽게 사라집니다.

---

## 📥 설치

> ⚠️ 참고: 프롬프트 관리자는 Gemini Enterprise 를 지원하는 유일한 기능입니다.

<div align="center">
  <a href="https://chromewebstore.google.com/detail/kjdpnimcnfinmilocccippmododhceol?utm_source=github&utm_medium=readme&utm_campaign=organic_growth&utm_content=ko" target="_blank">
    <img src="https://img.shields.io/badge/Chrome%20Web%20Store-4285F4?style=for-the-badge&logo=googlechrome&logoColor=white" alt="Chrome Web Store" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://microsoftedge.microsoft.com/addons/detail/gemini-voyager/gibmkggjijalcjinbdhcpklodjkhhlne" target="_blank">
    <img src="https://img.shields.io/badge/Microsoft%20Edge-0078D7?style=for-the-badge&logo=microsoftedge&logoColor=white" alt="Microsoft Edge Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://addons.mozilla.org/firefox/addon/gemini-voyager/" target="_blank">
    <img src="https://img.shields.io/badge/Firefox%20Add--ons-FF7139?style=for-the-badge&logo=firefox&logoColor=white" alt="Firefox Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://github.com/Nagi-ovo/gemini-voyager/releases/latest/" target="_blank">
    <img src="https://img.shields.io/badge/Safari-000000?style=for-the-badge&logo=safari&logoColor=white" alt="Safari 다운로드" height="36">
  </a>
</div>

<p align="center">
  <sub><b>Chrome 웹 스토어</b>는 Edge, Opera, Brave, Vivaldi, Arc 및 기타 Chromium 브라우저에서도 작동합니다.</sub>
</p>

> **스토어 상태:** Chrome ✅ · Firefox ✅ · Edge ✅ · Safari ✅

**수동 설치** 또는 **개발 빌드**에 대해서는 [설치 가이드](https://voyager.nagi.fun/ko/guide/installation)를 참조하세요.

---

## ☕ 프로젝트 후원하기

<div align="center">
  <a href="https://github.com/Nagi-ovo/gemini-voyager">
    <img src="https://raw.githubusercontent.com/Nagi-ovo/gemini-voyager/main/docs/public/assets/sponsors.svg" width="1000px" />
  </a>
</div>

Voyager 가 도움이 되었다면 커피 한 잔을 후원해 주세요. 지속적인 업데이트에 큰 도움이 됩니다! 후원자분들은 특별 감사 섹션에 기재됩니다. ❤️

<div align="center">
  <a href="https://www.buymeacoffee.com/Nag1ovo" target="_blank">
    <img src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" height="36">
  </a>
  <a href="https://github.com/sponsors/Nagi-ovo" target="_blank">
    <img src="https://img.shields.io/badge/Sponsor%20me-GitHub-ea4aaa?style=for-the-badge&logo=github&logoColor=white" alt="Sponsor on GitHub" height="36">
  </a>
  
  <p><b>또는 WeChat / Alipay / Afdian 을 통해 후원하기:</b></p>
  <table align="center" border="0" cellpadding="0" cellspacing="0">
    <tr>
      <td align="center">
        <img src="../docs/public/assets/wechat-sponsor.png" alt="WeChat Pay" height="160"><br>
        <sub><b>WeChat Pay</b></sub>
      </td>
      <td align="center">
        <img src="../docs/public/assets/alipay-sponsor.jpg" alt="Alipay" height="160"><br>
        <sub><b>Alipay</b></sub>
      </td>
      <td align="center">
        <a href="https://afdian.com/a/nagi-ovo" target="_blank">
          <picture>
            <source media="(prefers-color-scheme: dark)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo&bg_color=%230d1117&text_color=%23dedbd7&border_color=%232e343d" />
            <source media="(prefers-color-scheme: light)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" />
            <img alt="Nagi-ovo's Profile" src="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" height="160" />
          </picture>
        </a><br>
        <sub><b>Afdian</b></sub>
      </td>
    </tr>
  </table>
</div>

### 🎙️ 추천 도구: Typeless

Voyager 개발 중에 광범위하게 사용한 AI 음성 - 텍스트 변환 도구인 **[Typeless (typeless.com)](https://www.typeless.com/?via=gemini-voyager)**를 강력히 추천합니다. 이를 매일의 워크플로우에 통합함으로써 엄청난 시간을 절약하고 생산성을 크게 높일 수 있었습니다.

> 🎁 **[제 추천 링크를 통해 가입](https://www.typeless.com/?via=gemini-voyager)** (코드: _`gemini-voyager`_) 하시면 **$5 무료 크레딧**을 받으실 수 있습니다. 이는 제가 이 프로젝트를 계속 유지 관리할 수 있는 크레딧을 제공하며, 저의 작업을 지원하는 무료 방법입니다! ❤️

---

## 💬 커뮤니티

<table>
  <tr>
    <td align="center" width="50%" valign="top">
      <a href="https://x.com/Nag1ovo/status/2012610584722731188" target="_blank">
        <img src="https://img.shields.io/badge/Follow%20on-𝕏-000000?style=for-the-badge&logo=x&logoColor=white" alt="Follow on X" height="36">
      </a>
      <br><br>
      <b>업데이트 팔로우</b><br>
      <sub>최신 소식을 받아보세요.</sub>
    </td>
    <td align="center" width="50%" valign="top">
      <a href="https://discord.gg/TEUFxdMbGb" target="_blank">
        <img src="https://img.shields.io/discord/1463273957120675973?style=for-the-badge&logo=discord&logoColor=white&label=Join%20Discord" alt="Discord" height="36">
      </a>
      <br><br>
      <b>커뮤니티 가입</b><br>
      <sub>다른 사용자들과 대화하고, 프롬프트를 공유하고, 도움을 받으세요.</sub>
    </td>
  </tr>
</table>

---

## 🤝 기여 및 개발

[<img src="https://devin.ai/assets/askdeepwiki.png" alt="Ask DeepWiki" height="20"/>](https://deepwiki.com/Nagi-ovo/gemini-voyager)

여러분의 기여를 환영합니다! 버그 보고, 기능 제안, 문서 개선 또는 코드 제출 등 무엇이든 환영합니다:

- **이슈**: [버그 보고](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/bug_report.md) 또는 [기능 제안](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/feature_request.yml) 템플릿을 사용하세요.
- **풀 리퀘스트**: 가이드라인은 [CONTRIBUTING.md](../.github/CONTRIBUTING.md)를 확인하세요.

<details>
<summary>개발 설정</summary>

```bash
# 의존성 설치 (Bun 권장)
bun i

# 개발 모드 (자동 새로고침 지원)
bun run dev:chrome   # Chrome 및 Chromium 브라우저
bun run dev:firefox  # Firefox
bun run dev:safari   # Safari (macOS 필요)

# 프로덕션 빌드
bun run build:chrome   # Chrome
bun run build:firefox  # Firefox
bun run build:safari   # Safari
bun run build:all      # 모든 브라우저
```

**Safari 개발**: 추가 빌드 단계는 [safari/README.md](../safari/README.md) 를 참조하세요.

</details>

Voyager 를 더 좋게 만들 수 있도록 도와주셔서 감사합니다! ❤️

### ❤️ 특별 감사

Voyager에 기여해 주신 모든 기여자분들께 특별히 감사드립니다 ❤️

<a href="https://github.com/Nagi-ovo/gemini-voyager/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=Nagi-ovo/gemini-voyager&max=200&columns=14" />
</a>

---

## 🌟 크레딧

- **[DeepSeek Voyager](https://github.com/Azurboy/deepseek-voyager)** - Voyager 를 DeepSeek 에 맞게 조정한 포크 프로젝트로, DeepSeek 사용자에게 타임라인 탐색 및 채팅 관리 기능을 제공합니다!

- **[claude-nexus](https://github.com/Qiuner/claude-nexus)** - Voyager에서 영감을 받은 Claude.ai 향상 확장 프로그램으로, 타임라인 탐색, 폴더 관리, 프롬프트 라이브러리 등 다양한 기능을 제공하며 Voyager 프롬프트 가져오기/내보내기와 완전 호환됩니다!

- **[ChatGPT Conversation Timeline](https://github.com/Reborn14/chatgpt-conversation-timeline)** - 이 프로젝트에 영감을 준 ChatGPT 용 오리지널 타임라인 탐색 확장 프로그램입니다. Voyager 는 타임라인 개념을 Gemini 에 맞게 조정하고 폴더 관리, 프롬프트 저장소, 채팅 내보내기 등 광범위한 새로운 기능을 추가했습니다.

- **[Ophel Atlas](https://github.com/urzeye/ophel)** - AI 대화를 체계적이고 검색 가능한 문서로 변환하는 브라우저 확장 프로그램입니다. 자동 개요 생성, 대화 관리, 프롬프트 라이브러리를 제공하며 여러 AI 플랫폼을 지원합니다.

---

<div align="center">
  <a href="https://www.star-history.com/#Nagi-ovo/gemini-voyager&type=date&legend=top-left">
   <picture>
     <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&theme=dark&legend=top-left" />
     <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
     <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
   </picture>
  </a>
  <p>Made with ❤️ by Jesse Zhang</p>
  <sub>GPLv3 License © 2026</sub>
</div>


================================================
FILE: .github/README_PT.md
================================================
<div align="center">
  <img src="../docs/public/assets/promotion/Promo-Banner.png" alt="promotion"/>
  <h3>Torne a sua experiência Gemini™ verdadeiramente sua ✨</h3>
  <p>
    Navegação elegante na linha do tempo, organização de chats com pastas e o seu próprio cofre de prompts.<br>
    <b>É o "power-up" que faltava no Google Gemini.</b>
  </p>
  
  <p>
    <img src="https://img.shields.io/badge/Chrome-✓-4285F4?style=flat-square&logo=googlechrome&logoColor=white" alt="Chrome">
    <img src="https://img.shields.io/badge/Edge-✓-0078D7?style=flat-square&logo=microsoftedge&logoColor=white" alt="Edge">
    <img src="https://img.shields.io/badge/Firefox-✓-FF7139?style=flat-square&logo=firefox&logoColor=white" alt="Firefox">
    <img src="https://img.shields.io/badge/Safari-✓-000000?style=flat-square&logo=safari&logoColor=white" alt="Safari">
    <img src="https://img.shields.io/badge/Opera-✓-FF1B2D?style=flat-square&logo=opera&logoColor=white" alt="Opera">
    <img src="https://img.shields.io/badge/Brave-✓-FB542B?style=flat-square&logo=brave&logoColor=white" alt="Brave">
  </p>
  <p>
    <img src="https://img.shields.io/github/stars/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub stars">
    <img src="https://img.shields.io/github/forks/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub forks">
    <img src="https://img.shields.io/github/v/release/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="Latest version">
    <img src="https://img.shields.io/github/downloads/Nagi-ovo/gemini-voyager/total?style=flat-square&logo=github" alt="GitHub downloads">
    <img src="https://img.shields.io/chrome-web-store/users/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome Web Store users">
    <img src="https://img.shields.io/chrome-web-store/rating/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome Web Store rating">
    <img src="https://img.shields.io/amo/users/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox Add-ons users">
    <img src="https://img.shields.io/amo/rating/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox Add-ons rating">
  </p>
  <p>
    <a href="https://trendshift.io/repositories/16094" target="_blank"><img src="https://trendshift.io/api/badge/repositories/16094" alt="Nagi-ovo%2Fgemini-voyager | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
    <a href="https://www.producthunt.com/products/gemini-voyager?embed=true&amp;utm_source=badge-featured&amp;utm_medium=badge&amp;utm_campaign=badge-gemini-voyager" target="_blank" rel="noopener noreferrer"><img alt="Gemini Voyager - All-in-one Gemini suite: folders, chat export and much more | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1064704&amp;theme=light&amp;t=1768842096186"></a>
  </p>
  <p align="center">
    ✨ Estamos no Product Hunt! Gostaríamos muito de ouvir o seu feedback. ❤️
  </p>
</div>

<p align="center">
  <a href="https://voyager.nagi.fun/pt">📖 Documentação</a> • 
  <a href="../README.md">English</a> • 
  <a href="./README_ZH.md">简体中文</a> •
  <a href="./README_ZH_TW.md">繁體中文</a> •
  <a href="./README_JA.md">日本語</a> •
  <a href="./README_FR.md">Français</a> •
  <a href="./README_ES.md">Español</a> •
  <a href="./README_RU.md">Русский</a> •
  <a href="./README_AR.md">العربية</a> •
  <a href="./README_KO.md">한국어</a>
</p>

<p align="center">
    <img src="https://count.getloli.com/@gemini-voyager?name=gemini-voyager&theme=rule34&padding=7&offset=0&align=top&scale=1&pixelated=1&darkmode=auto" width="400">
  </p>

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024507762483277927?s=20" target="_blank">
    <img src="../docs/public/assets/x-recommendation.png" alt="KOL Recommendation" width="500">
  </a>
  <br>
  <b>🎉 Altamente recomendado pelos principais influenciadores de tecnologia!</b>
</p>

> [!IMPORTANT]
> **Aviso de mudança de nome**: Devido a problemas de marcas registradas e direitos autorais, esta extensão foi oficialmente renomeada para **Voyager**. No entanto, devido ao processo extremamente lento de revisão do Chrome Web Store, a mudança de nome não foi aprovada em 7 dias — está temporariamente indisponível no Chrome Web Store.

> [!NOTE]
> Se o Voyager for útil para você, compartilhe no X, Reddit, YouTube, etc. Cada partilha ajuda mais pessoas a descobrir o projeto e a melhorar a experiência com Gemini. Obrigado.

---

## 👋 Porquê o Voyager?

Nós adoramos o Gemini, mas às vezes desejaríamos que ele tivesse apenas um pouco mais de "estrutura".

Foi por isso que criámos o **Voyager**. Não é apenas uma ferramenta; é um companheiro que o ajuda a manter as suas conversas com IA organizadas, acessíveis e produtivas. Quer seja um investigador que lida com dezenas de threads, um programador que guarda snippets de código, ou apenas alguém que adora ordem, o Voyager foi desenhado para si.

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024509398601597412?s=20" target="_blank">
    <img src="../docs/public/assets/try-voyager.png" alt="Try Voyager" width="500">
  </a>
  <br>
  <i>Durante o problema de 18 de fevereiro, em que o Google Gemini App tornou as conversas históricas de alguns usuários inacessíveis, os usuários do Voyager ainda conseguiram ver suas conversas salvas em suas pastas.</i>
</p>

---

## ✨ Funcionalidades

<div align="center">
  <img src="../docs/public/assets/teaser.png" alt="teaser"/>
</div>

Para um guia completo, visite a nossa [Documentação](https://voyager.nagi.fun/pt).

### 🌌 Núcleo Comum (Gemini & AI Studio)

- **📂 [Organização por Pastas](https://voyager.nagi.fun/pt/guide/folders)**: Organize os seus chats numa hierarquia de dois níveis com suporte para **arrastar e largar** e **sincronização com o Google Drive**.
  - **Gemini**: Suporta o **Modo de Isolamento de Conta** e **Cores de Pastas Personalizadas**.
- **💡 [Cofre de Prompts](https://voyager.nagi.fun/pt/guide/prompts)**: Guarde e reutilize os seus melhores prompts no Gemini, AI Studio e [sites personalizados](https://voyager.nagi.fun/pt/guide/custom-websites).
- **☁️ [Sincronização na Nuvem](https://voyager.nagi.fun/pt/guide/cloud-sync)**: Sincronize as suas pastas e cofre de prompts com o Google Drive.
- **📐 Cópia de Fórmulas**: Cópia com um clique de códigos-fonte LaTeX e MathML (Word).
- **🌦️ Efeitos Visuais**: Adicione um ambiente sazonal com **neve**, **chuva cinematográfica** ou **pétalas de sakura** a partir do painel de configurações.

### ✨ Recursos Exclusivos do Gemini

- **📍 [Navegação na Linha do Tempo](https://voyager.nagi.fun/pt/guide/timeline)**: Nós visuais para saltar entre mensagens, marcar momentos importantes e gerir ramos da conversa.
- **💾 [Exportação de Chat](https://voyager.nagi.fun/pt/guide/export)**: Exporte conversas para JSON, Markdown ou PDF com imagens incluídas.
- **🧜‍♀️ [Renderização Mermaid](https://voyager.nagi.fun/pt/guide/mermaid)**: Renderização automática de fluxogramas, diagramas de sequência e outros gráficos Mermaid.
- **📝 [Correção de Renderização Markdown](https://voyager.nagi.fun/pt/guide/markdown-fix)**: Corrige automaticamente a sintaxe de negrito do Markdown quebrada pelos elementos HTML injetados pelo Gemini.
- **🍌 [NanoBanana](https://voyager.nagi.fun/pt/guide/nanobanana)**: Remoção de marca de água sem perdas para imagens geradas pelo Gemini.
- **🔬 [Deep Research](https://voyager.nagi.fun/pt/guide/deep-research)**: Extraia processos de pensamento e links de pesquisa de sessões de Deep Research.
- **🛠️ Ferramentas de Produtividade**:
  - **[Exclusão em Lote](https://voyager.nagi.fun/pt/guide/batch-delete)**: Limpe o seu histórico em massa.
  - **[Resposta com Citação](https://voyager.nagi.fun/pt/guide/quote-reply)**: Responda com contexto selecionando o texto.
  - **[Sincronização do Título da Aba](https://voyager.nagi.fun/pt/guide/tab-title)**: Sincroniza automaticamente o título da aba do navegador.
  - **[Prevenir rolamento automático](https://voyager.nagi.fun/pt/guide/prevent-auto-scroll)**: Intercepta o comportamento de salto indesejado ao enviar uma mensagem.
  - **[Colapso de Entrada](https://voyager.nagi.fun/pt/guide/input-collapse)**: Área de entrada auto-colapsável para mais espaço de leitura.
  - **[Modelo Padrão](https://voyager.nagi.fun/pt/guide/default-model)**: Defina o seu modelo favorito como padrão.
  - **[Ocultar itens recentes e Gems](https://voyager.nagi.fun/pt/guide/recents-hider)**: Oculta a lista "Recentes" na barra lateral para reduzir distrações.

### 🎨 Personalização

- Abra o popup da extensão e encontre **Efeitos Visuais** para alternar entre `Desligado`, `Neve`, `Sakura` e `Chuva`.
- Os efeitos são renderizados como sobreposições leves em tela cheia e não bloqueiam a interação com a página.
- Ao trocar de efeito ou desativar, as partículas desaparecem naturalmente em vez de sumir abruptamente.

---

## 📥 Instalação

> ⚠️ Nota: O Gestor de Prompts é a única funcionalidade que suporta o Gemini for Enterprise.

<div align="center">
  <a href="https://chromewebstore.google.com/detail/kjdpnimcnfinmilocccippmododhceol?utm_source=github&utm_medium=readme&utm_campaign=organic_growth&utm_content=pt" target="_blank">
    <img src="https://img.shields.io/badge/Chrome%20Web%20Store-4285F4?style=for-the-badge&logo=googlechrome&logoColor=white" alt="Chrome Web Store" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://microsoftedge.microsoft.com/addons/detail/gemini-voyager/gibmkggjijalcjinbdhcpklodjkhhlne" target="_blank">
    <img src="https://img.shields.io/badge/Microsoft%20Edge-0078D7?style=for-the-badge&logo=microsoftedge&logoColor=white" alt="Microsoft Edge Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://addons.mozilla.org/firefox/addon/gemini-voyager/" target="_blank">
    <img src="https://img.shields.io/badge/Firefox%20Add--ons-FF7139?style=for-the-badge&logo=firefox&logoColor=white" alt="Firefox Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://github.com/Nagi-ovo/gemini-voyager/releases/latest/" target="_blank">
    <img src="https://img.shields.io/badge/Safari-000000?style=for-the-badge&logo=safari&logoColor=white" alt="Safari Descarregar" height="36">
  </a>
</div>

<p align="center">
  <sub>A <b>Chrome Web Store</b> também funciona no Edge, Opera, Brave, Vivaldi, Arc e outros navegadores Chromium.</sub>
</p>

> **Estado da Loja:** Chrome ✅ · Firefox ✅ · Edge ✅ · Safari ✅

Para **instalação manual** ou **builds de desenvolvimento**, consulte o [Guia de Instalação](https://voyager.nagi.fun/pt/guide/installation).

---

## ☕ Apoie este Projeto

<div align="center">
  <a href="https://github.com/Nagi-ovo/gemini-voyager">
    <img src="https://raw.githubusercontent.com/Nagi-ovo/gemini-voyager/main/docs/public/assets/sponsors.svg" width="1000px" />
  </a>
</div>

Se o Voyager facilita a sua vida, considere pagar-me um café. Ajuda a manter as atualizações! Os patrocinadores serão destacados na nossa secção de Agradecimentos Especiais. ❤️

<div align="center">
  <a href="https://www.buymeacoffee.com/Nag1ovo" target="_blank">
    <img src="https://img.shields.io/badge/Pague-me%20um%20café-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" height="36">
  </a>
  <a href="https://github.com/sponsors/Nagi-ovo" target="_blank">
    <img src="https://img.shields.io/badge/Patrocine-me%20no%20GitHub-ea4aaa?style=for-the-badge&logo=github&logoColor=white" alt="Sponsor on GitHub" height="36">
  </a>
  
  <p><b>Ou apoie via WeChat / Alipay / Afdian:</b></p>
  <table align="center" border="0" cellpadding="0" cellspacing="0">
    <tr>
      <td align="center">
        <img src="../docs/public/assets/wechat-sponsor.png" alt="WeChat Pay" height="160"><br>
        <sub><b>WeChat Pay</b></sub>
      </td>
      <td align="center">
        <img src="../docs/public/assets/alipay-sponsor.jpg" alt="Alipay" height="160"><br>
        <sub><b>Alipay</b></sub>
      </td>
      <td align="center">
        <a href="https://afdian.com/a/nagi-ovo" target="_blank">
          <picture>
            <source media="(prefers-color-scheme: dark)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo&bg_color=%230d1117&text_color=%23dedbd7&border_color=%232e343d" />
            <source media="(prefers-color-scheme: light)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" />
            <img alt="Nagi-ovo's Profile" src="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" height="160" />
          </picture>
        </a><br>
        <sub><b>Afdian</b></sub>
      </td>
    </tr>
  </table>
</div>

### 🎙️ Ferramenta Recomendada: Typeless

Recomendo vivamente o **[Typeless (typeless.com)](https://www.typeless.com/?via=gemini-voyager)**, uma ferramenta de voz para texto com IA que utilizei extensivamente durante o desenvolvimento do Voyager. Poupou-me imenso tempo e aumentou significativamente a minha produtividade.

> 🎁 **[Registe-se através do meu link](https://www.typeless.com/?via=gemini-voyager)** (Código: _`gemini-voyager`_) para obter **5$ de créditos gratuitos**. ❤️

---

## 🤝 Contribuição e Desenvolvimento

[<img src="https://devin.ai/assets/askdeepwiki.png" alt="Ask DeepWiki" height="20"/>](https://deepwiki.com/Nagi-ovo/gemini-voyager)

Damos as boas-vindas a contribuições!

- **Issues**: Use os nossos templates de [relatório de erros](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/bug_report.md) ou [pedido de funcionalidade](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/feature_request.yml).
- **Pull Requests**: Consulte [CONTRIBUTING.md](./CONTRIBUTING.md).

Obrigado por ajudar a tornar o Voyager melhor! ❤️

### ❤️ Agradecimentos Especiais

Um agradecimento especial a todos os colaboradores pelas suas contribuições ao Voyager ❤️

<a href="https://github.com/Nagi-ovo/gemini-voyager/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=Nagi-ovo/gemini-voyager&max=200&columns=14" />
</a>

---

## 🌟 Créditos

- **[DeepSeek Voyager](https://github.com/Azurboy/deepseek-voyager)** - Um fork do Voyager adaptado para o DeepSeek.

- **[claude-nexus](https://github.com/Qiuner/claude-nexus)** - Uma extensão de aprimoramento para Claude.ai inspirada no Voyager, com navegação por linha do tempo, gerenciamento de pastas, biblioteca de prompts e muito mais, com compatibilidade total de importação/exportação de prompts com o Voyager!

- **[ChatGPT Conversation Timeline](https://github.com/Reborn14/chatgpt-conversation-timeline)** - A fonte original de inspiração para a navegação na linha do tempo.

- **[Ophel Atlas](https://github.com/urzeye/ophel)** - Uma extensão de navegador que transforma conversas de IA em documentos organizados e pesquisáveis, com geração automática de esquemas, gestão de conversas e biblioteca de prompts, compatível com múltiplas plataformas de IA.

---

<div align="center">
  <a href="https://www.star-history.com/#Nagi-ovo/gemini-voyager&type=date&legend=top-left">
   <picture>
     <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&theme=dark&legend=top-left" />
     <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
     <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
   </picture>
  </a>
  <p>Feito com ❤️ por Jesse Zhang</p>
  <sub>GPLv3 License © 2026</sub>
</div>


================================================
FILE: .github/README_RU.md
================================================
<div align="center">
  <img src="../docs/public/assets/promotion/Promo-Banner.png" alt="promotion"/>
  <h3>Сделайте ваш Gemini™ по-настоящему вашим ✨</h3>
  <p>
    Элегантная навигация по таймлайну, организация чатов по папкам и собственное хранилище промптов.<br>
    <b>Это недостающий элемент для Google Gemini.</b>
  </p>
  
  <p>
    <img src="https://img.shields.io/badge/Chrome-✓-4285F4?style=flat-square&logo=googlechrome&logoColor=white" alt="Chrome">
    <img src="https://img.shields.io/badge/Edge-✓-0078D7?style=flat-square&logo=microsoftedge&logoColor=white" alt="Edge">
    <img src="https://img.shields.io/badge/Firefox-✓-FF7139?style=flat-square&logo=firefox&logoColor=white" alt="Firefox">
    <img src="https://img.shields.io/badge/Safari-✓-000000?style=flat-square&logo=safari&logoColor=white" alt="Safari">
    <img src="https://img.shields.io/badge/Opera-✓-FF1B2D?style=flat-square&logo=opera&logoColor=white" alt="Opera">
    <img src="https://img.shields.io/badge/Brave-✓-FB542B?style=flat-square&logo=brave&logoColor=white" alt="Brave">
  </p>
  <p>
    <img src="https://img.shields.io/github/stars/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub stars">
    <img src="https://img.shields.io/github/forks/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub forks">
    <img src="https://img.shields.io/github/v/release/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="Latest version">
    <img src="https://img.shields.io/github/downloads/Nagi-ovo/gemini-voyager/total?style=flat-square&logo=github" alt="GitHub downloads">
    <img src="https://img.shields.io/chrome-web-store/users/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome Web Store users">
    <img src="https://img.shields.io/chrome-web-store/rating/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome Web Store rating">
    <img src="https://img.shields.io/amo/users/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox Add-ons users">
    <img src="https://img.shields.io/amo/rating/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox Add-ons rating">
  </p>
  <p>
    <a href="https://trendshift.io/repositories/16094" target="_blank"><img src="https://trendshift.io/api/badge/repositories/16094" alt="Nagi-ovo%2Fgemini-voyager | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
    <a href="https://www.producthunt.com/products/gemini-voyager?embed=true&amp;utm_source=badge-featured&amp;utm_medium=badge&amp;utm_campaign=badge-gemini-voyager" target="_blank" rel="noopener noreferrer"><img alt="Gemini Voyager - All-in-one Gemini suite: folders, chat export and much more | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1064704&amp;theme=light&amp;t=1768842096186"></a>
  </p>
  <p align="center">
    ✨ Мы на Product Hunt! Будем рады вашим отзывам. ❤️
  </p>
</div>

<p align="center">
  <a href="https://voyager.nagi.fun/ru">📖 Документация</a> • 
  <a href="../README.md">English</a> • 
  <a href="./README_ZH.md">简体中文</a> •
  <a href="./README_ZH_TW.md">繁體中文</a> •
  <a href="./README_JA.md">日本語</a> •
  <a href="./README_FR.md">Français</a> •
  <a href="./README_ES.md">Español</a> •
  <a href="./README_PT.md">Português</a> •
  <a href="./README_AR.md">العربية</a> •
  <a href="./README_KO.md">한국어</a>
</p>

<p align="center">
    <img src="https://count.getloli.com/@gemini-voyager?name=gemini-voyager&theme=rule34&padding=7&offset=0&align=top&scale=1&pixelated=1&darkmode=auto" width="400">
  </p>

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024507762483277927?s=20" target="_blank">
    <img src="../docs/public/assets/x-recommendation.png" alt="KOL Recommendation" width="500">
  </a>
  <br>
  <b>🎉 Настоятельно рекомендуется ведущими технологическими лидерами мнений!</b>
</p>

> [!IMPORTANT]
> **Уведомление о переименовании**: В связи с проблемами товарных знаков и авторских прав это расширение официально переименовано в **Voyager**. Однако из-за крайне медленного процесса проверки Chrome Web Store обновление названия не было одобрено в течение 7 дней — расширение временно недоступно в Chrome Web Store.

> [!NOTE]
> Если Voyager вам полезен, поделитесь им в X, Reddit, YouTube и т.д. Каждый репост помогает большему числу людей узнать о проекте и улучшать опыт использования Gemini. Спасибо.

---

## 👋 Почему Voyager?

Мы любим Gemini, но иногда хочется, чтобы в нем было чуть больше порядка.

Именно поэтому мы создали **Voyager**. Это не просто инструмент, а помощник, который помогает организовать ваши диалоги с ИИ, сделать их доступными и продуктивными. Будь вы исследователем, ведущим десятки веток, разработчиком, сохраняющим фрагменты кода, или просто любителем порядка — Voyager создан для вас.

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024509398601597412?s=20" target="_blank">
    <img src="../docs/public/assets/try-voyager.png" alt="Try Voyager" width="500">
  </a>
  <br>
  <i>Во время сбоя 18 февраля, когда приложение Google Gemini сделало исторические разговоры некоторых пользователей недоступными, пользователи Voyager по-прежнему могли видеть свои сохраненные разговоры в своих папках.</i>
</p>

---

## ✨ Возможности

### 🌌 Общие функции (Gemini & AI Studio)

- **📂 [Организация по папкам](https://voyager.nagi.fun/ru/guide/folders)**: Группируйте чаты в двухуровневую иерархию папок с поддержкой **перетаскивания** и **синхронизации с Google Drive**.
  - **Gemini**: Поддержка **режима изоляции аккаунтов** и **пользовательских цветов папок**.
- **💡 [Хранилище промптов](https://voyager.nagi.fun/ru/guide/prompts)**: Сохраняйте и повторно используйте лучшие промпты в Gemini, AI Studio и на [любых сайтах](https://voyager.nagi.fun/ru/guide/custom-websites).
- **☁️ [Облачная синхронизация](https://voyager.nagi.fun/ru/guide/cloud-sync)**: Синхронизируйте папки и хранилище промптов с Google Drive.
- **📐 Копирование формул**: Копирование исходного кода LaTeX и MathML (Word) в один клик.
- **🌦️ Визуальные эффекты**: Добавьте сезонную атмосферу с **снегом**, **кинематографическим дождём** или **падающими лепестками сакуры** из панели настроек.

### ✨ Эксклюзивные функции Gemini

- **📍 [Навигация по таймлайну](https://voyager.nagi.fun/ru/guide/timeline)**: Визуальные узлы для быстрого перехода между сообщениями, отметки важных моментов и управления ветками диалога.
- **💾 [Экспорт чатов](https://voyager.nagi.fun/ru/guide/export)**: Сохраняйте диалоги в форматах JSON, Markdown или PDF вместе с изображениями.
- **🧜‍♀️ [Рендеринг Mermaid](https://voyager.nagi.fun/ru/guide/mermaid)**: Автоматический рендеринг блок-схем, диаграмм последовательности и других графиков Mermaid.
- **📝 [Исправление рендеринга Markdown](https://voyager.nagi.fun/ru/guide/markdown-fix)**: Автоматическое исправление синтаксиса жирного шрифта Markdown, нарушенного вставленными Gemini HTML-элементами.
- **🍌 [NanoBanana](https://voyager.nagi.fun/ru/guide/nanobanana)**: Автоматическое удаление водяных знаков с изображений, созданных Gemini, без потери качества.
- **🔬 [Deep Research](https://voyager.nagi.fun/ru/guide/deep-research)**: Извлечение цепочек рассуждений и ссылок из сессий Deep Research.
- **🛠️ Инструменты продуктивности**:
  - **[Пакетное удаление](https://voyager.nagi.fun/ru/guide/batch-delete)**: Массовая очистка истории диалогов.
  - **[Ответ с цитированием](https://voyager.nagi.fun/ru/guide/quote-reply)**: Цитирование выделенного текста при ответе.
  - **[Синхронизация заголовка](https://voyager.nagi.fun/ru/guide/tab-title)**: Автоматическое обновление названия вкладки браузера.
  - **[Предотвращение автопрокрутки](https://voyager.nagi.fun/ru/guide/prevent-auto-scroll)**: Блокирует прыжок страницы при отправке сообщения.
  - **[Сворачивание ввода](https://voyager.nagi.fun/ru/guide/input-collapse)**: Сворачивание пустого поля ввода для увеличения области чтения.
  - **[Модель по умолчанию](https://voyager.nagi.fun/ru/guide/default-model)**: Установите вашу любимую модель по умолчанию.
  - **[Скрытие недавних элементов и Gems](https://voyager.nagi.fun/ru/guide/recents-hider)**: Скройте список «Недавние» на боковой панели, чтобы не отвлекаться.

### 🎨 Персонализация

- Откройте всплывающее окно расширения и найдите **Визуальные эффекты**, чтобы переключаться между `Выключено`, `Снег`, `Сакура` и `Дождь`.
- Эффекты отображаются как лёгкие полноэкранные наложения и не блокируют взаимодействие со страницей.
- При переключении или отключении эффекта частицы плавно исчезают, а не пропадают мгновенно.

---

## 📥 Установка

> ⚠️ Примечание: Prompt Manager — единственная функция, поддерживающая Gemini для предприятий.

<div align="center">
  <a href="https://chromewebstore.google.com/detail/kjdpnimcnfinmilocccippmododhceol?utm_source=github&utm_medium=readme&utm_campaign=organic_growth&utm_content=ru" target="_blank">
    <img src="https://img.shields.io/badge/Chrome%20Web%20Store-4285F4?style=for-the-badge&logo=googlechrome&logoColor=white" alt="Chrome Web Store" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://microsoftedge.microsoft.com/addons/detail/gemini-voyager/gibmkggjijalcjinbdhcpklodjkhhlne" target="_blank">
    <img src="https://img.shields.io/badge/Microsoft%20Edge-0078D7?style=for-the-badge&logo=microsoftedge&logoColor=white" alt="Microsoft Edge Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://addons.mozilla.org/firefox/addon/gemini-voyager/" target="_blank">
    <img src="https://img.shields.io/badge/Firefox%20Add--ons-FF7139?style=for-the-badge&logo=firefox&logoColor=white" alt="Firefox Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://github.com/Nagi-ovo/gemini-voyager/releases/latest/" target="_blank">
    <img src="https://img.shields.io/badge/Safari-000000?style=for-the-badge&logo=safari&logoColor=white" alt="Safari Скачать" height="36">
  </a>
</div>

<p align="center">
  <sub>Версия для <b>Chrome Web Store</b> также работает в Edge, Opera, Brave, Vivaldi, Arc и других браузерах на базе Chromium.</sub>
</p>

> **Статус в магазинах:** Chrome ✅ · Firefox ✅ · Edge ✅ · Safari ✅

Для **ручной установки** или **сборки для разработки**, пожалуйста, обратитесь к [Руководству по установке](https://voyager.nagi.fun/ru/guide/installation).

---

## ☕ Поддержать проект

<div align="center">
  <a href="https://github.com/Nagi-ovo/gemini-voyager">
    <img src="https://raw.githubusercontent.com/Nagi-ovo/gemini-voyager/main/docs/public/assets/sponsors.svg" width="1000px" />
  </a>
</div>

Если Voyager делает вашу жизнь проще, вы можете угостить меня кофе. Это помогает продолжать работу над обновлениями! Спонсоры будут отмечены в разделе благодарностей. ❤️

<div align="center">
  <a href="https://www.buymeacoffee.com/Nag1ovo" target="_blank">
    <img src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" height="36">
  </a>
  <a href="https://github.com/sponsors/Nagi-ovo" target="_blank">
    <img src="https://img.shields.io/badge/Sponsor%20me-GitHub-ea4aaa?style=for-the-badge&logo=github&logoColor=white" alt="Sponsor on GitHub" height="36">
  </a>
  
  <p><b>Или через WeChat / Alipay / Afdian:</b></p>
  <table align="center" border="0" cellpadding="0" cellspacing="0">
    <tr>
      <td align="center">
        <img src="../docs/public/assets/wechat-sponsor.png" alt="WeChat Pay" height="160"><br>
        <sub><b>WeChat Pay</b></sub>
      </td>
      <td align="center">
        <img src="../docs/public/assets/alipay-sponsor.jpg" alt="Alipay" height="160"><br>
        <sub><b>Alipay</b></sub>
      </td>
      <td align="center">
        <a href="https://afdian.com/a/nagi-ovo" target="_blank">
          <picture>
            <source media="(prefers-color-scheme: dark)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo&bg_color=%230d1117&text_color=%23dedbd7&border_color=%232e343d" />
            <source media="(prefers-color-scheme: light)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" />
            <img alt="Nagi-ovo's Profile" src="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" height="160" />
          </picture>
        </a><br>
        <sub><b>Afdian</b></sub>
      </td>
    </tr>
  </table>
</div>

### 🎙️ Рекомендуемый инструмент: Typeless

Я настоятельно рекомендую **[Typeless (typeless.com)](https://www.typeless.com/?via=gemini-voyager)** — инструмент для перевода голоса в текст с ИИ, который я активно использовал во время разработки Voyager.

> 🎁 **[Присоединяйтесь по моей ссылке](https://www.typeless.com/?via=gemini-voyager)** (Код: _`gemini-voyager`_), чтобы получить **$5 бесплатных бонусов**. ❤️

---

## 🤝 Участие в разработке

[<img src="https://devin.ai/assets/askdeepwiki.png" alt="Ask DeepWiki" height="20"/>](https://deepwiki.com/Nagi-ovo/gemini-voyager)

Мы приветствуем любую помощь!

- **Issues**: Используйте наши шаблоны для [отчетов об ошибках](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/bug_report.md) или [предложений новых функций](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/feature_request.yml).
- **Pull Requests**: Ознакомьтесь с [CONTRIBUTING.md](./CONTRIBUTING.md).

Спасибо, что помогаете делать Voyager лучше! ❤️

### ❤️ Особая Благодарность

Особая благодарность всем участникам за их вклад в Voyager ❤️

<a href="https://github.com/Nagi-ovo/gemini-voyager/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=Nagi-ovo/gemini-voyager&max=200&columns=14" />
</a>

---

## 🌟 Благодарности

- **[DeepSeek Voyager](https://github.com/Azurboy/deepseek-voyager)** - Форк Voyager, адаптированный для DeepSeek.

- **[claude-nexus](https://github.com/Qiuner/claude-nexus)** - Расширение для улучшения Claude.ai, вдохновлённое Voyager: навигация по таймлайну, управление папками, библиотека промптов и многое другое, с полной совместимостью импорта/экспорта промптов с Voyager!

- **[ChatGPT Conversation Timeline](https://github.com/Reborn14/chatgpt-conversation-timeline)** - Оригинальное расширение с таймлайном для ChatGPT, вдохновившее этот проект.

- **[Ophel Atlas](https://github.com/urzeye/ophel)** - Расширение браузера, которое превращает диалоги с ИИ в организованные и доступные для поиска документы с автоматическим созданием оглавлений, управлением диалогами и библиотекой промптов, поддерживающее несколько платформ ИИ.

---

<div align="center">
  <a href="https://www.star-history.com/#Nagi-ovo/gemini-voyager&type=date&legend=top-left">
   <picture>
     <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&theme=dark&legend=top-left" />
     <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
     <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
   </picture>
  </a>
  <p>Сделано с ❤️ Jesse Zhang</p>
  <sub>GPLv3 License © 2026</sub>
</div>


================================================
FILE: .github/README_ZH.md
================================================
<div align="center">
  <img src="../docs/public/assets/promotion/Promo-Banner-cn.png" alt="promotion"/>
  <h3>打造属于你的 Gemini™ 体验 ✨</h3>
  <p>
    优雅的时间轴导航、文件夹管理对话、构建专属提示词库。<br>
    <b>这是 Google Gemini 缺失的那块拼图。</b>
  </p>
  
  <p>
    <img src="https://img.shields.io/badge/Chrome-✓-4285F4?style=flat-square&logo=googlechrome&logoColor=white" alt="Chrome">
    <img src="https://img.shields.io/badge/Edge-✓-0078D7?style=flat-square&logo=microsoftedge&logoColor=white" alt="Edge">
    <img src="https://img.shields.io/badge/Firefox-✓-FF7139?style=flat-square&logo=firefox&logoColor=white" alt="Firefox">
    <img src="https://img.shields.io/badge/Safari-✓-000000?style=flat-square&logo=safari&logoColor=white" alt="Safari">
    <img src="https://img.shields.io/badge/Opera-✓-FF1B2D?style=flat-square&logo=opera&logoColor=white" alt="Opera">
    <img src="https://img.shields.io/badge/Brave-✓-FB542B?style=flat-square&logo=brave&logoColor=white" alt="Brave">
  </p>
  <p>
    <img src="https://img.shields.io/github/stars/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub Star">
    <img src="https://img.shields.io/github/forks/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub Fork">
    <img src="https://img.shields.io/github/v/release/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="最新版本">
    <img src="https://img.shields.io/github/downloads/Nagi-ovo/gemini-voyager/total?style=flat-square&logo=github" alt="GitHub 下载量">
    <img src="https://img.shields.io/chrome-web-store/users/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome 商店用户数">
    <img src="https://img.shields.io/chrome-web-store/rating/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome 商店评分">
    <img src="https://img.shields.io/amo/users/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox 商店用户数">
    <img src="https://img.shields.io/amo/rating/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox 商店评分">
  </p>
  <p>
    <a href="https://trendshift.io/repositories/16094" target="_blank"><img src="https://trendshift.io/api/badge/repositories/16094" alt="Nagi-ovo%2Fgemini-voyager | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
    <a href="https://www.producthunt.com/products/gemini-voyager?embed=true&amp;utm_source=badge-featured&amp;utm_medium=badge&amp;utm_campaign=badge-gemini-voyager" target="_blank" rel="noopener noreferrer"><img alt="Gemini Voyager - All-in-one Gemini suite: folders, chat export and much more | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1064704&amp;theme=light&amp;t=1768842096186"></a>
  </p>
  <p align="center">
    ✨ 我们已在 Product Hunt 上线!欢迎来分享你的想法和反馈。❤️
  </p>
</div>

<p align="center">
  <a href="https://voyager.nagi.fun">📖 文档</a> • 
  <a href="../README.md">English</a> • 
  <a href="./README_ZH_TW.md">繁體中文</a> •
  <a href="./README_JA.md">日本語</a> •
  <a href="./README_FR.md">Français</a> •
  <a href="./README_ES.md">Español</a> •
  <a href="./README_PT.md">Português</a> •
  <a href="./README_RU.md">Русский</a> •
  <a href="./README_AR.md">العربية</a> •
  <a href="./README_KO.md">한국어</a>
</p>

<p align="center">
    <img src="https://count.getloli.com/@gemini-voyager?name=gemini-voyager&theme=rule34&padding=7&offset=0&align=top&scale=1&pixelated=1&darkmode=auto" width="400">
  </p>

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024507762483277927?s=20" target="_blank">
    <img src="../docs/public/assets/x-recommendation.png" alt="KOL Recommendation" width="500">
  </a>
  <br>
  <b>🎉 感谢知名科技圈大 V 与社区的强烈推荐!</b>
</p>

> [!IMPORTANT]
> **改名公告**:由于商标版权问题,本插件已正式改名为 **Voyager**。但由于谷歌插件商店审核速度奇慢,七天内未能完成名称更新审核,暂时无法在 Chrome Web Store 使用。

> [!NOTE]
> 如果 Voyager 帮到了你,欢迎分享到 X、即刻、小红书、Linux.do、V2EX 等等,也欢迎推荐给海外 KOL。每一次分享都能让更多人看到这个项目,从而改善 Gemini 的使用体验。谢谢。

---

## 👋 为什么开发 Voyager?

我们都很喜欢 Gemini,但有时候总觉得它少了一点"秩序感"。

这就是我们开发 **Voyager** 的初衷。它不仅仅是一个工具,更是一个能帮你把 AI 对话变得井井有条、触手可及的得力助手。无论你是需要处理大量对话的研究人员,还是喜欢收藏代码片段的开发者,亦或是单纯的整理控,Voyager 都是为你准备的。

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024509398601597412?s=20" target="_blank">
    <img src="../docs/public/assets/try-voyager.png" alt="Try Voyager" width="500">
  </a>
  <br>
  <i>在 2 月 18 号 Google Gemini App 导致部分用户历史对话无法访问的问题中,Voyager 的用户仍然能够在其文件夹中看到被保存下来的对话。</i>
</p>

---

## ✨ 功能特性

### 🌌 通用核心 (Gemini & AI Studio)

- **📂 [文件夹管理](https://voyager.nagi.fun/guide/folders)**: 支持 **多级目录**、**拖拽排序** 及 **Google Drive 同步**。
  - **Gemini**: 支持 **多账号隔离模式** 及 **自定义文件夹颜色**。
- **💡 [提示词库](https://voyager.nagi.fun/guide/prompts)**: 跨平台同步提示词,支持 Gemini、AI Studio 及 [自定义网站](https://voyager.nagi.fun/guide/custom-websites)。
- **☁️ [云同步](https://voyager.nagi.fun/guide/cloud-sync)**: 支持将文件夹和提示词库同步到 Google Drive。
- **📐 公式复制**: 一键复制 LaTeX 和 MathML (Word) 源码。
- **🌦️ 视觉特效**: 在设置面板里一键切换 **飘雪**、**电影感雨滴** 或 **樱花飘落**,给页面增加季节氛围。

### ✨ Gemini 专属增强

- **📍 [时间线导航](https://voyager.nagi.fun/guide/timeline)**: 可视化节点,瞬间跳转消息,星标重点,管理对话分支。
- **💾 [对话导出](https://voyager.nagi.fun/guide/export)**: 支持导出为 JSON、Markdown 或 PDF(含图片)。
- **🧜‍♀️ [Mermaid 渲染](https://voyager.nagi.fun/guide/mermaid)**: 自动渲染流程图、时序图等 Mermaid 图表。
- **📝 [Markdown 渲染修复](https://voyager.nagi.fun/guide/markdown-fix)**: 自动修复 Gemini 注入 HTML 导致的 Markdown 加粗失效问题。
- **🍌 [NanoBanana](https://voyager.nagi.fun/guide/nanobanana)**: 自动去除 Gemini 生成图片的无损水印。
- **🔬 [Deep Research](https://voyager.nagi.fun/guide/deep-research)**: 一键提取 Deep Research 对话的思考过程和研究链接。
- **🛠️ 效率工具**:
  - **[批量删除](https://voyager.nagi.fun/guide/batch-delete)**: 批量清理对话记录。
  - **[引用回复](https://voyager.nagi.fun/guide/quote-reply)**: 选中对话文本即可一键引用回复。
  - **[标签页标题同步](https://voyager.nagi.fun/guide/tab-title)**: 自动将标签页标题设为对话标题。
  - **[防自动跳转](https://voyager.nagi.fun/guide/prevent-auto-scroll)**: 拦截每次发送新问题后页面强制滚动到底部的内置行为,找回丝滑体验。
  - **[输入框折叠](https://voyager.nagi.fun/guide/input-collapse)**: 输入框自动收纳,释放阅读空间。
  - **[默认模型](https://voyager.nagi.fun/guide/default-model)**: 为新对话设置默认选中的模型。
  - **[隐藏最近项目](https://voyager.nagi.fun/guide/recents-hider)**: 隐藏侧边栏的“最近”列表,减少干扰。
  - **隐藏升级提醒**: 自动隐藏 Gemini 侧边栏和模型切换菜单中的“升级到 Google AI Ultra”按钮(默认开启)。

### 🎨 个性化体验

- 点击插件图标,在设置中的 **视觉特效** 里可切换 `关闭`、`飘雪`、`樱花`、`雨`。
- 特效以轻量全屏覆盖层呈现,不会阻挡页面交互。
- 切换特效或关闭时,粒子会自然退场,不会突兀消失。

---

## 📥 安装方式

> ⚠️ 注意:提示词管理器是唯一支持 Gemini 企业版的功能。

<div align="center">
  <a href="https://chromewebstore.google.com/detail/kjdpnimcnfinmilocccippmododhceol?utm_source=github&utm_medium=readme&utm_campaign=organic_growth&utm_content=zh" target="_blank">
    <img src="https://img.shields.io/badge/Chrome%20应用商店-4285F4?style=for-the-badge&logo=googlechrome&logoColor=white" alt="Chrome 应用商店" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://microsoftedge.microsoft.com/addons/detail/gemini-voyager/gibmkggjijalcjinbdhcpklodjkhhlne" target="_blank">
    <img src="https://img.shields.io/badge/Microsoft%20Edge-0078D7?style=for-the-badge&logo=microsoftedge&logoColor=white" alt="Microsoft Edge Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://addons.mozilla.org/firefox/addon/gemini-voyager/" target="_blank">
    <img src="https://img.shields.io/badge/Firefox%20Add--ons-FF7139?style=for-the-badge&logo=firefox&logoColor=white" alt="Firefox Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://github.com/Nagi-ovo/gemini-voyager/releases/latest/" target="_blank">
    <img src="https://img.shields.io/badge/Safari-000000?style=for-the-badge&logo=safari&logoColor=white" alt="Safari 下载" height="36">
  </a>
</div>

<p align="center">
  <sub><b>Chrome 应用商店</b>同样适用于 Edge、Opera、Brave、Vivaldi、Arc 等 Chromium 浏览器。</sub>
</p>

> **商店状态:** Chrome ✅ · Firefox ✅ · Edge ✅ · Safari ✅

关于 **手动安装** 或 **开发构建**,请参阅 [安装指南](https://voyager.nagi.fun/guide/installation)。

---

## ☕ 支持本项目

<div align="center">
  <a href="https://github.com/Nagi-ovo/gemini-voyager">
    <img src="https://raw.githubusercontent.com/Nagi-ovo/gemini-voyager/main/docs/public/assets/sponsors.svg" width="1000px" />
  </a>
</div>

如果 Voyager 提升了你的体验,欢迎请我喝杯咖啡。赞助者将被列入致谢名单。❤️

<div align="center">
  <a href="https://www.buymeacoffee.com/Nag1ovo" target="_blank">
    <img src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" height="36">
  </a>
  <a href="https://github.com/sponsors/Nagi-ovo" target="_blank">
    <img src="https://img.shields.io/badge/Sponsor%20me-GitHub-ea4aaa?style=for-the-badge&logo=github&logoColor=white" alt="Sponsor on GitHub" height="36">
  </a>
  
  <p><b>或通过微信 / 支付宝 / 爱发电支持:</b></p>
  <table align="center" border="0" cellpadding="0" cellspacing="0">
    <tr>
      <td align="center">
        <img src="../docs/public/assets/wechat-sponsor.png" alt="微信支付" height="160"><br>
        <sub><b>微信支付</b></sub>
      </td>
      <td align="center">
        <img src="../docs/public/assets/alipay-sponsor.jpg" alt="支付宝" height="160"><br>
        <sub><b>支付宝</b></sub>
      </td>
      <td align="center">
        <a href="https://afdian.com/a/nagi-ovo" target="_blank">
          <picture>
            <source media="(prefers-color-scheme: dark)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo&bg_color=%230d1117&text_color=%23dedbd7&border_color=%232e343d" />
            <source media="(prefers-color-scheme: light)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" />
            <img alt="Nagi-ovo's Profile" src="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" height="160" />
          </picture>
        </a><br>
        <sub><b>爱发电</b></sub>
      </td>
    </tr>
  </table>
</div>

### 🎙️ 特别推荐: Typeless

我非常推荐 **[Typeless (typeless.com)](https://www.typeless.com/?via=gemini-voyager)**,一款 AI 语音转文字工具。我在开发过程中一直在使用它,极大地提升了我的工作效率。

> 🎁 **[点击我的邀请链接](https://www.typeless.com/?via=gemini-voyager)**(邀请码 _`gemini-voyager`_)可获得 **5 美元免费额度**。这也是支持本项目的一种免费方式!❤️

---

## 💬 交流与反馈

<table>
  <tr>
    <td align="center" width="50%" valign="top">
      <a href="https://x.com/Nag1ovo/status/2012609459663634589" target="_blank">
        <img src="https://img.shields.io/badge/关注-𝕏-000000?style=for-the-badge&logo=x&logoColor=white" alt="关注 X" height="36">
      </a>
      <br><br>
      <b>关注动态</b><br>
      <sub>获取最新动态。</sub>
    </td>
    <td align="center" width="50%" valign="top">
      <a href="https://discord.gg/TEUFxdMbGb" target="_blank">
        <img src="https://img.shields.io/discord/1463273957120675973?style=for-the-badge&logo=discord&logoColor=white&label=加入%20Discord" alt="Discord" height="36">
      </a>
      <br><br>
      <b>加入社区</b><br>
      <sub>与其他用户交流、分享提示词、获取帮助。</sub>
    </td>
  </tr>
</table>

---

## 🤝 参与贡献与开发

[<img src="https://devin.ai/assets/askdeepwiki.png" alt="Ask DeepWiki" height="20"/>](https://deepwiki.com/Nagi-ovo/gemini-voyager)

欢迎参与贡献!

- **Issue**:使用 [Bug 报告](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/bug_report.yml) 或 [功能请求](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/feature_request.yml) 模板。
- **Pull Request**:请查看 [贡献指南](./CONTRIBUTING.md)。

<details>
<summary>开发环境配置</summary>

```bash
# 安装依赖 (推荐 Bun)
bun i

# 开发模式
bun run dev:chrome
bun run dev:firefox
bun run dev:safari

# 生产构建
bun run build:chrome
bun run build:firefox
bun run build:safari
bun run build:all
```

**Safari 开发**:详见 [safari/README.md](../safari/README.md)。

</details>

感谢你让 Voyager 变得更好!❤️

### ❤️ 特别感谢

特别感谢所有为 Voyager 做出贡献的贡献者们 ❤️

<a href="https://github.com/Nagi-ovo/gemini-voyager/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=Nagi-ovo/gemini-voyager&max=200&columns=14" />
</a>

---

## 🌟 致谢

- **[DeepSeek Voyager](https://github.com/Azurboy/deepseek-voyager)** - 为 DeepSeek 适配的 Fork 版本。

- **[claude-nexus](https://github.com/Qiuner/claude-nexus)** - 受 Voyager 启发的 Claude.ai 增强扩展,提供时间线导航、文件夹管理、提示词库等功能,并与 Voyager 的提示词导入/导出完全兼容!

- **[ChatGPT Conversation Timeline](https://github.com/Reborn14/chatgpt-conversation-timeline)** - 本项目的灵感来源。

- **[Ophel Atlas](https://github.com/urzeye/ophel)** - 将 AI 对话转化为有组织、可搜索文档的浏览器扩展,支持自动生成大纲、对话管理和提示词库,兼容多个 AI 平台。

---

<div align="center">
  <a href="https://www.star-history.com/#Nagi-ovo/gemini-voyager&type=date&legend=top-left">
   <picture>
     <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&theme=dark&legend=top-left" />
     <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
     <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
   </picture>
  </a>
  <p>Made with ❤️ by Jesse Zhang</p>
  <sub>GPLv3 License © 2026</sub>
</div>


================================================
FILE: .github/README_ZH_TW.md
================================================
<div align="center">
  <img src="../docs/public/assets/promotion/Promo-Banner-cn.png" alt="promotion"/>
  <h3>打造屬於你的 Gemini™ 體驗 ✨</h3>
  <p>
    優雅的時間軸導航、資料夾管理對話、構建專屬提示詞庫。<br>
    <b>這是 Google Gemini 缺失的那塊拼圖。</b>
  </p>
  
  <p>
    <img src="https://img.shields.io/badge/Chrome-✓-4285F4?style=flat-square&logo=googlechrome&logoColor=white" alt="Chrome">
    <img src="https://img.shields.io/badge/Edge-✓-0078D7?style=flat-square&logo=microsoftedge&logoColor=white" alt="Edge">
    <img src="https://img.shields.io/badge/Firefox-✓-FF7139?style=flat-square&logo=firefox&logoColor=white" alt="Firefox">
    <img src="https://img.shields.io/badge/Safari-✓-000000?style=flat-square&logo=safari&logoColor=white" alt="Safari">
    <img src="https://img.shields.io/badge/Opera-✓-FF1B2D?style=flat-square&logo=opera&logoColor=white" alt="Opera">
    <img src="https://img.shields.io/badge/Brave-✓-FB542B?style=flat-square&logo=brave&logoColor=white" alt="Brave">
  </p>
  <p>
    <img src="https://img.shields.io/github/stars/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub Star">
    <img src="https://img.shields.io/github/forks/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="GitHub Fork">
    <img src="https://img.shields.io/github/v/release/Nagi-ovo/gemini-voyager?style=flat-square&logo=github" alt="最新版本">
    <img src="https://img.shields.io/github/downloads/Nagi-ovo/gemini-voyager/total?style=flat-square&logo=github" alt="GitHub 下載量">
    <img src="https://img.shields.io/chrome-web-store/users/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome 商店用戶數">
    <img src="https://img.shields.io/chrome-web-store/rating/kjdpnimcnfinmilocccippmododhceol?style=flat-square&logo=google-chrome" alt="Chrome 商店評分">
    <img src="https://img.shields.io/amo/users/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox 商店用戶數">
    <img src="https://img.shields.io/amo/rating/gemini-voyager?style=flat-square&logo=firefox" alt="Firefox 商店評分">
  </p>
  <p>
    <a href="https://trendshift.io/repositories/16094" target="_blank"><img src="https://trendshift.io/api/badge/repositories/16094" alt="Nagi-ovo%2Fgemini-voyager | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
    <a href="https://www.producthunt.com/products/gemini-voyager?embed=true&amp;utm_source=badge-featured&amp;utm_medium=badge&amp;utm_campaign=badge-gemini-voyager" target="_blank" rel="noopener noreferrer"><img alt="Gemini Voyager - All-in-one Gemini suite: folders, chat export and much more | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1064704&amp;theme=light&amp;t=1768842096186"></a>
  </p>
  <p align="center">
    ✨ 我們已在 Product Hunt 上線!歡迎來分享你的想法和回饋。❤️
  </p>
</div>

<p align="center">
  <a href="https://voyager.nagi.fun/zh_TW">📖 文檔</a> • 
  <a href="../README.md">English</a> • 
  <a href="./README_ZH.md">简体中文</a> •
  <a href="./README_JA.md">日本語</a> •
  <a href="./README_FR.md">Français</a> •
  <a href="./README_ES.md">Español</a> •
  <a href="./README_PT.md">Português</a> •
  <a href="./README_RU.md">Русский</a> •
  <a href="./README_AR.md">العربية</a> •
  <a href="./README_KO.md">한국어</a>
</p>

<p align="center">
    <img src="https://count.getloli.com/@gemini-voyager?name=gemini-voyager&theme=rule34&padding=7&offset=0&align=top&scale=1&pixelated=1&darkmode=auto" width="400">
  </p>

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024507762483277927?s=20" target="_blank">
    <img src="../docs/public/assets/x-recommendation.png" alt="KOL Recommendation" width="500">
  </a>
  <br>
  <b>🎉 感謝知名科技圈大 V 與社區的強烈推薦!</b>
</p>

> [!IMPORTANT]
> **改名公告**:由於商標版權問題,本插件已正式改名為 **Voyager**。但由於 Chrome Web Store 審核速度極慢,七天內未能完成名稱更新審核,暫時無法在 Chrome Web Store 使用。

> [!NOTE]
> 如果 Voyager 有幫助,歡迎分享到 X、Facebook、YouTube、Threads、Dcard 等等。每一次分享都能讓更多人看見這個專案,從而改善 Gemini 的使用體驗。謝謝。

---

## 👋 為什麼開發 Voyager?

我們都很喜歡 Gemini,但有時候總覺得它少了一點"秩序感"。

這就是我們開發 **Voyager** 的初衷。它不僅僅是一個工具,更是一個能幫你把 AI 對話變得井井有條、觸手可及的得力助手。無論你是需要處理大量對話的研究人員,還是喜歡收藏代碼片段的開發者,亦或是單純的整理控,Voyager 都是為你準備的。

<p align="center">
  <a href="https://x.com/Nag1ovo/status/2024509398601597412?s=20" target="_blank">
    <img src="../docs/public/assets/try-voyager.png" alt="Try Voyager" width="500">
  </a>
  <br>
  <i>在 2 月 18 號 Google Gemini App 導致部分用戶歷史對話無法訪問的問題中,Voyager 的用戶仍然能夠在其資料夾中看到被保存下來的對話。</i>
</p>

---

## ✨ 功能特性

<div align="center">
  <img src="../docs/public/assets/teaser.png" alt="teaser"/>
</div>

查看完整功能,請訪問我們的 [官方文檔](https://voyager.nagi.fun/zh_TW)。

### 🌌 通用核心 (Gemini & AI Studio)

- **📂 [資料夾管理](https://voyager.nagi.fun/zh_TW/guide/folders)**: 支持 **多級目錄**、**拖拽排序** 及 **Google Drive 同步**。
  - **Gemini**: 支持 **多帳號隔離模式** 及 **自定義資料夾顏色**。
- **💡 [提示詞庫](https://voyager.nagi.fun/zh_TW/guide/prompts)**: 跨平台同步提示詞,支持 Gemini、AI Studio 及 [自定義網站](https://voyager.nagi.fun/zh_TW/guide/custom-websites)。
- **☁️ [雲同步](https://voyager.nagi.fun/zh_TW/guide/cloud-sync)**: 支持將資料夾和提示詞庫同步到 Google Drive。
- **📐 公式複製**: 一鍵複製 LaTeX 和 MathML (Word) 源碼。
- **🌦️ 視覺特效**: 在設置面板中一鍵切換 **飄雪**、**電影感雨滴** 或 **櫻花飄落**,給頁面增添季節氛圍。

### ✨ Gemini 專屬增強

- **📍 [時間線導航](https://voyager.nagi.fun/zh_TW/guide/timeline)**: 可視化節點,瞬間跳轉訊息,星標重點,管理對話分支。
- **💾 [對話導出](https://voyager.nagi.fun/zh_TW/guide/export)**: 支持導出為 JSON、Markdown 或 PDF(含圖片)。
- **🧜‍♀️ [Mermaid 圖表渲染](https://voyager.nagi.fun/zh_TW/guide/mermaid)**: 自動渲染流程圖、時序圖等 Mermaid 圖表。
- **📝 [Markdown 渲染修復](https://voyager.nagi.fun/zh_TW/guide/markdown-fix)**: 自動修復 Gemini 注入 HTML 導致的 Markdown 加粗失效問題。
- **🍌 [NanoBanana](https://voyager.nagi.fun/zh_TW/guide/nanobanana)**: 自動去除 Gemini 生成圖片的無損浮水印。
- **🔬 [Deep Research](https://voyager.nagi.fun/zh_TW/guide/deep-research)**: 一鍵提取 Deep Research 對話的思考過程和研究鏈接。
- **🛠️ 效率工具**:
  - **[批量刪除](https://voyager.nagi.fun/zh_TW/guide/batch-delete)**: 批量清理對話記錄。
  - **[引用回覆](https://voyager.nagi.fun/zh_TW/guide/quote-reply)**: 選中對話文本即可一鍵引用回覆。
  - **[標籤頁標題同步](https://voyager.nagi.fun/zh_TW/guide/tab-title)**: 自動將標籤頁標題設為對話標題。
  - **[防自動跳轉](https://voyager.nagi.fun/zh_TW/guide/prevent-auto-scroll)**: 攔截每次發送新問題後頁面強制滾動到最底部的內建行為,找回絲滑體驗。
  - **[輸入框摺疊](https://voyager.nagi.fun/zh_TW/guide/input-collapse)**: 輸入框自動收納,釋放閱讀空間。
  - **[預設模型](https://voyager.nagi.fun/zh_TW/guide/default-model)**: 為新對話設置預設選中的模型。
  - **[隱藏最近項目和 Gem](https://voyager.nagi.fun/zh_TW/guide/recents-hider)**: 隱藏側邊欄的”最近”列表,減少干擾。

### 🎨 個性化體驗

- 點擊插件圖標,在設定中的 **視覺特效** 裡可切換 `關閉`、`飄雪`、`櫻花`、`雨`。
- 特效以輕量全螢幕覆蓋層呈現,不會阻擋頁面互動。
- 切換特效或關閉時,粒子會自然退場,不會突兀消失。

---

## 📥 安裝方式

> ⚠️ 注意:提示詞管理器是唯一支持 Gemini 企業版的功能。

<div align="center">
  <a href="https://chromewebstore.google.com/detail/kjdpnimcnfinmilocccippmododhceol?utm_source=github&utm_medium=readme&utm_campaign=organic_growth&utm_content=zh_tw" target="_blank">
    <img src="https://img.shields.io/badge/Chrome%20應用商店-4285F4?style=for-the-badge&logo=googlechrome&logoColor=white" alt="Chrome 應用商店" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://microsoftedge.microsoft.com/addons/detail/gemini-voyager/gibmkggjijalcjinbdhcpklodjkhhlne" target="_blank">
    <img src="https://img.shields.io/badge/Microsoft%20Edge-0078D7?style=for-the-badge&logo=microsoftedge&logoColor=white" alt="Microsoft Edge Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://addons.mozilla.org/firefox/addon/gemini-voyager/" target="_blank">
    <img src="https://img.shields.io/badge/Firefox%20Add--ons-FF7139?style=for-the-badge&logo=firefox&logoColor=white" alt="Firefox Add-ons" height="36">
  </a>
  &nbsp;&nbsp;
  <a href="https://github.com/Nagi-ovo/gemini-voyager/releases/latest/" target="_blank">
    <img src="https://img.shields.io/badge/Safari-000000?style=for-the-badge&logo=safari&logoColor=white" alt="Safari 下載" height="36">
  </a>
</div>

<p align="center">
  <sub><b>Chrome 應用商店</b>同樣適用於 Edge、Opera、Brave、Vivaldi、Arc 等 Chromium 瀏覽器。</sub>
</p>

> **商店狀態:** Chrome ✅ · Firefox ✅ · Edge ✅ · Safari ✅

關於 **手動安裝** 或 **開發構建**,請參閱 [安裝指南](https://voyager.nagi.fun/zh_TW/guide/installation)。

---

## ☕ 支持本項目

<div align="center">
  <a href="https://github.com/Nagi-ovo/gemini-voyager">
    <img src="https://raw.githubusercontent.com/Nagi-ovo/gemini-voyager/main/docs/public/assets/sponsors.svg" width="1000px" />
  </a>
</div>

如果 Voyager 提升了你的體驗,歡迎請我喝杯咖啡。贊助者將被列入致謝名單。❤️

<div align="center">
  <a href="https://www.buymeacoffee.com/Nag1ovo" target="_blank">
    <img src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" height="36">
  </a>
  <a href="https://github.com/sponsors/Nagi-ovo" target="_blank">
    <img src="https://img.shields.io/badge/Sponsor%20me-GitHub-ea4aaa?style=for-the-badge&logo=github&logoColor=white" alt="Sponsor on GitHub" height="36">
  </a>
  
  <p><b>或通過微信 / 支付寶 / 愛發電支持:</b></p>
  <table align="center" border="0" cellpadding="0" cellspacing="0">
    <tr>
      <td align="center">
        <img src="../docs/public/assets/wechat-sponsor.png" alt="微信支付" height="160"><br>
        <sub><b>微信支付</b></sub>
      </td>
      <td align="center">
        <img src="../docs/public/assets/alipay-sponsor.jpg" alt="支付寶" height="160"><br>
        <sub><b>支付寶</b></sub>
      </td>
      <td align="center">
        <a href="https://afdian.com/a/nagi-ovo" target="_blank">
          <picture>
            <source media="(prefers-color-scheme: dark)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo&bg_color=%230d1117&text_color=%23dedbd7&border_color=%232e343d" />
            <source media="(prefers-color-scheme: light)" srcset="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" />
            <img alt="Nagi-ovo's Profile" src="https://afdian-connect.deno.dev/profile.svg?slug=nagi-ovo" height="160" />
          </picture>
        </a><br>
        <sub><b>愛發電</b></sub>
      </td>
    </tr>
  </table>
</div>

### 🎙️ 特別推薦: Typeless

我非常推薦 **[Typeless (typeless.com)](https://www.typeless.com/?via=gemini-voyager)**,一款 AI 語音轉文字工具。我在開發過程中一直在使用它,極大地提升了我的工作效率。

> 🎁 **[點擊我的邀請鏈接](https://www.typeless.com/?via=gemini-voyager)**(邀請碼 _`gemini-voyager`_)可獲得 **5 美元免費額度**。這也是支持本項目的一種免費方式!❤️

---

## 💬 交流與回饋

<table>
  <tr>
    <td align="center" width="50%" valign="top">
      <a href="https://x.com/Nag1ovo/status/2012609459663634589" target="_blank">
        <img src="https://img.shields.io/badge/關注-𝕏-000000?style=for-the-badge&logo=x&logoColor=white" alt="關注 X" height="36">
      </a>
      <br><br>
      <b>關注動態</b><br>
      <sub>獲取最新動態。</sub>
    </td>
    <td align="center" width="50%" valign="top">
      <a href="https://discord.gg/TEUFxdMbGb" target="_blank">
        <img src="https://img.shields.io/discord/1463273957120675973?style=for-the-badge&logo=discord&logoColor=white&label=加入%20Discord" alt="Discord" height="36">
      </a>
      <br><br>
      <b>加入社區</b><br>
      <sub>與其他用戶交流、分享提示詞、獲取幫助。</sub>
    </td>
  </tr>
</table>

---

## 🤝 參與貢獻與開發

[<img src="https://devin.ai/assets/askdeepwiki.png" alt="Ask DeepWiki" height="20"/>](https://deepwiki.com/Nagi-ovo/gemini-voyager)

歡迎參與貢獻!

- **Issue**:使用 [Bug 報告](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/bug_report.yml) 或 [功能請求](https://github.com/Nagi-ovo/gemini-voyager/blob/main/.github/ISSUE_TEMPLATE/feature_request.yml) 模板。
- **Pull Request**:請查看 [貢獻指南](./CONTRIBUTING.md)。

<details>
<summary>開發環境配置</summary>

```bash
# 安裝依賴 (推薦 Bun)
bun i

# 開發模式
bun run dev:chrome
bun run dev:firefox
bun run dev:safari

# 生產構建
bun run build:chrome
bun run build:firefox
bun run build:safari
bun run build:all
```

**Safari 開發**:詳見 [safari/README.md](../safari/README.md)。

</details>

感謝你讓 Voyager 變得更好!❤️

### ❤️ 特別感謝

特別感謝所有為 Voyager 做出貢獻的貢獻者們 ❤️

<a href="https://github.com/Nagi-ovo/gemini-voyager/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=Nagi-ovo/gemini-voyager&max=200&columns=14" />
</a>

---

## 🌟 致謝

- **[DeepSeek Voyager](https://github.com/Azurboy/deepseek-voyager)** - 為 DeepSeek 適配的 Fork 版本。

- **[claude-nexus](https://github.com/Qiuner/claude-nexus)** - 受 Voyager 啟發的 Claude.ai 增強擴充套件,提供時間軸導覽、資料夾管理、提示詞庫等功能,並與 Voyager 的提示詞匯入/匯出完全相容!

- **[ChatGPT Conversation Timeline](https://github.com/Reborn14/chatgpt-conversation-timeline)** - 本項目的靈感來源。

- **[Ophel Atlas](https://github.com/urzeye/ophel)** - 將 AI 對話轉化為有組織、可搜尋文件的瀏覽器擴充功能,支援自動生成大綱、對話管理和提示詞庫,相容多個 AI 平台。

---

<div align="center">
  <a href="https://www.star-history.com/#Nagi-ovo/gemini-voyager&type=date&legend=top-left">
   <picture>
     <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&theme=dark&legend=top-left" />
     <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
     <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Nagi-ovo/gemini-voyager&type=date&legend=top-left" />
   </picture>
  </a>
  <p>Made with ❤️ by Jesse Zhang</p>
  <sub>GPLv3 License © 2026</sub>
</div>


================================================
FILE: .github/RELEASE_TEMPLATE.md
================================================
## ✨ What's New

<!-- Add main features here -->

---

## 🐛 Bug Fixes

<!-- Add bug fixes here -->

---

## 📚 Documentation

<!-- Add documentation updates here -->

---

## 📥 Installation

Download the latest version for your browser:

- **Chrome/Edge/Opera/Brave**: `gemini-voyager-chrome-{VERSION}.zip`
- **Firefox**: `gemini-voyager-firefox-{VERSION}.zip`
- **Safari**: `gemini-voyager-{VERSION}.dmg`

See [README](https://github.com/Nagi-ovo/gemini-voyager#-installation) for installation instructions.

---

**Full Changelog**: https://github.com/Nagi-ovo/gemini-voyager/compare/{PREV_VERSION}...{VERSION}


================================================
FILE: .github/docs/CHANGELOG.md
================================================
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- **Safari browser support** 🎉
  - Safari build configuration and development mode
  - Installation guide ([EN](safari/INSTALLATION.md) | [中文](safari/INSTALLATION_ZH.md))
  - Development guide ([EN](.../../../safari/README.md) | [中文](.../../../safari/README_ZH.md))
  - New commands: `build:safari`, `dev:safari`, `build:all`
- **Conversation export (Markdown/PDF)**
  - Rich Markdown export with formulas, code blocks, tables, lists, headings
  - Auto-package images: if a chat contains user-uploaded images, export a ZIP with `chat.md` and `assets/` (images rewritten to relative paths)
  - PDF export: inline images (best-effort) and print-optimized styles
  - Background service worker for cross-origin image fetch; added host permissions for Google image domains

### Changed

- **Cross-browser compatibility**
  - Migrated to `browser.*` API via `webextension-polyfill` for better compatibility
  - All storage APIs now use async/await pattern
- **Export robustness**
  - More resilient DOM extraction (supports Angular/custom elements; better selectors)
  - Reduced noisy logs; cleaner fallback paths
  - PDF images constrained (max-width ~60%) to avoid oversized visuals

### Fixed

- **Dependencies**
  - Downgraded `marked` to v11 for compatibility
  - Upgraded `@typescript-eslint/eslint-plugin` to v8
  - Resolved peer dependency conflicts
- **Export correctness**
  - Fixed duplicate inclusion of code blocks/tables in Markdown
  - Fixed export button causing navigation back to `/app` on Gemini
  - Addressed missing assistant content by adding last-chance plaintext fallback
  - Avoid CORS failures for images in Markdown by packaging images into ZIP (with relative paths)

### Supported Browsers

- **Chromium** (Chrome, Edge, Opera, Brave, Vivaldi, Arc)
- **Gecko** (Firefox)
- **WebKit** (Safari) ⭐ NEW

## [0.6.1] - Previous Release

### Features

- Interactive conversation timeline
- Folder management
- Prompt library with search
- Chat export to JSON
- Cross-tab star sync
- Markdown/KaTeX rendering
- Multi-language (EN, 中文)

---

## Migration Notes

### Users

- Chrome/Firefox: No changes needed
- Safari: See [installation guide](.github/docs/safari/INSTALLATION.md)

### Developers

- API changed from `chrome.*` to `browser.*`
- Storage now uses Promises (async/await)
- New Safari build commands available


================================================
FILE: .github/docs/IMPORT_EXPORT_GUIDE.md
================================================
# Folder Import/Export Guide

## Overview

The folder configuration import/export feature allows you to sync folder configurations across devices without setting up a server.

## How to Use

### 📥 Export Folder Configuration (Download ⬇️)

1. Open Gemini chat page
2. Find the **download icon button** (downward arrow ⬇️) in the folder area
3. Click to download the configuration file (format: `gemini-voyager-folders-YYYYMMDD-HHMMSS.json`)

### 📤 Import Folder Configuration (Upload ⬆️)

1. Click the **upload icon button** (upward arrow ⬆️) in the folder area
2. Choose an import strategy:
   - **Merge Mode**: Keep existing folders, only add new ones (recommended)
   - **Overwrite Mode**: Completely replace existing configuration (creates backup)
3. Select a previously exported JSON file
4. Click "Import" to confirm

## Import Strategies

### Merge Mode

- ✅ Keeps all existing folders and conversations
- ✅ Only adds new folders and conversations
- ✅ Automatically skips duplicates (by ID)
- 💡 Best for: Importing partial configurations from other devices

### Overwrite Mode

- ⚠️ Deletes all existing folders
- ✅ Completely uses imported configuration
- 🔒 Automatically creates backup (stored in sessionStorage)
- 💡 Best for: Full sync to a new device

## 🔄 Backup & Recovery

### Backup Information

- **Auto Backup**: Automatically created during overwrite import
- **Storage Location**: Browser sessionStorage (temporary storage)
- **Validity**: Valid until current tab is closed
- **Size Limit**: Usually 5-10MB

### How to Restore Backup (Console Operation)

If you encounter issues after import, you can restore the backup while the tab is still open:

```javascript
// 1. Open browser console (F12)

// 2. Check if backup exists
const hasBackup = sessionStorage.getItem('gvFolderBackup');
console.log('Backup exists:', hasBackup !== null);

// 3. View backup time
const backupTime = sessionStorage.getItem('gvFolderBackupTimestamp');
console.log('Backup time:', backupTime);

// 4. Restore backup
const backup = JSON.parse(sessionStorage.getItem('gvFolderBackup'));
localStorage.setItem('gvFolderData', JSON.stringify(backup));

// 5. Refresh page
location.reload();
```

### Clear Backup

```javascript
sessionStorage.removeItem('gvFolderBackup');
sessionStorage.removeItem('gvFolderBackupTimestamp');
```

## Export File Format

```json
{
  "format": "gemini-voyager.folders.v1",
  "exportedAt": "2025-01-15T10:30:00.000Z",
  "version": "0.7.2",
  "data": {
    "folders": [
      {
        "id": "folder-xxx",
        "name": "My Folder",
        "parentId": null,
        "isExpanded": true,
        "createdAt": 1736935800000,
        "updatedAt": 1736935800000
      }
    ],
    "folderContents": {
      "folder-xxx": [
        {
          "conversationId": "conv-yyy",
          "title": "Conversation Title",
          "url": "https://gemini.google.com/app/...",
          "addedAt": 1736935800000
        }
      ]
    }
  }
}
```

## Data Security

- ✅ **Local Storage**: All data is stored locally only, not uploaded to any server
- ✅ **Format Validation**: Strict data format validation during import to prevent corruption
- ✅ **Auto Backup**: Automatic backup before overwrite operations
- ✅ **Version Control**: Files include version numbers for future compatibility

## FAQ

### Q: Where is the backup stored?

A: Stored in the browser's `sessionStorage`, only valid for the current tab, automatically cleared when the tab is closed.

### Q: Why can't I see the backup file?

A: The backup is temporarily stored in memory and does not generate a file. For permanent storage, use the export feature.

### Q: Can it sync automatically?

A: Currently requires manual export/import. Automatic sync would require cloud service support, which is not provided to protect privacy.

### Q: What if the configuration is wrong after import?

A: If the tab is still open, you can restore the backup through the console (see instructions above).

### Q: Does it support cross-browser sync?

A: Yes! Simply export from one browser and import to another.

## Best Practices

1. **Regular Exports**: Make it a habit to export configurations regularly
2. **Cloud Backup**: Save exported JSON files to cloud storage
3. **Test Import**: Test with merge mode first on new devices
4. **Keep Backups**: Export a backup before important operations
5. **Version Management**: Save multiple versions for different configuration states

## Technical Details

- **Format Version**: `gemini-voyager.folders.v1`
- **Deduplication Strategy**: Deduplicate by `id` and `conversationId`
- **File Encoding**: UTF-8
- **Max File Size**: Theoretically unlimited (limited by browser memory)
- **Compatibility**: Chrome 88+, Firefox 85+, Safari 14+

## Feedback & Support

For issues or suggestions, please visit:
https://github.com/Nagi-ovo/gemini-voyager/issues/36


================================================
FILE: .github/docs/IMPORT_EXPORT_GUIDE_ZH.md
================================================
# 文件夹导出/导入功能使用指南

## 功能概述

文件夹配置导出/导入功能允许您在不同设备间同步文件夹配置,无需搭建服务器。

## 使用方法

### 📥 导出文件夹配置(下载 ⬇️)

1. 打开 Gemini 聊天页面
2. 在文件夹区域找到**下载图标按钮**(向下箭头 ⬇️)
3. 点击即可下载配置文件(格式:`gemini-voyager-folders-YYYYMMDD-HHMMSS.json`)

### 📤 导入文件夹配置(上传 ⬆️)

1. 点击文件夹区域的**上传图标按钮**(向上箭头 ⬆️)
2. 选择导入策略:
   - **合并模式**:保留现有文件夹,只添加新的(推荐)
   - **覆盖模式**:完全替换现有配置(会创建备份)
3. 选择之前导出的 JSON 文件
4. 点击"导入"按钮确认

## 导入策略说明

### 合并模式 (Merge)

- ✅ 保留所有现有文件夹和对话
- ✅ 只添加新的文件夹和对话
- ✅ 自动跳过重复项(按 ID 判断)
- 💡 适合:从其他设备导入部分配置

### 覆盖模式 (Overwrite)

- ⚠️ 删除所有现有文件夹
- ✅ 完全使用导入的配置
- 🔒 自动创建备份(存储在 sessionStorage)
- 💡 适合:完全同步到新设备

## 🔄 备份与恢复

### 备份说明

- **自动备份**:覆盖导入时自动创建
- **存储位置**:浏览器 sessionStorage(临时存储)
- **有效期**:当前标签页关闭前有效
- **大小限制**:通常为 5-10MB

### 如何恢复备份(控制台操作)

如果导入后发现问题,可以在当前标签页未关闭的情况下恢复备份:

```javascript
// 1. 打开浏览器控制台 (F12)

// 2. 检查是否有备份
const hasBackup = sessionStorage.getItem('gvFolderBackup');
console.log('备份存在:', hasBackup !== null);

// 3. 查看备份时间
const backupTime = sessionStorage.getItem('gvFolderBackupTimestamp');
console.log('备份时间:', backupTime);

// 4. 恢复备份
const backup = JSON.parse(sessionStorage.getItem('gvFolderBackup'));
localStorage.setItem('gvFolderData', JSON.stringify(backup));

// 5. 刷新页面
location.reload();
```

### 清除备份

```javascript
sessionStorage.removeItem('gvFolderBackup');
sessionStorage.removeItem('gvFolderBackupTimestamp');
```

## 导出文件格式

```json
{
  "format": "gemini-voyager.folders.v1",
  "exportedAt": "2025-01-15T10:30:00.000Z",
  "version": "0.7.2",
  "data": {
    "folders": [
      {
        "id": "folder-xxx",
        "name": "我的文件夹",
        "parentId": null,
        "isExpanded": true,
        "createdAt": 1736935800000,
        "updatedAt": 1736935800000
      }
    ],
    "folderContents": {
      "folder-xxx": [
        {
          "conversationId": "conv-yyy",
          "title": "对话标题",
          "url": "https://gemini.google.com/app/...",
          "addedAt": 1736935800000
        }
      ]
    }
  }
}
```

## 数据安全

- ✅ **本地存储**:所有数据仅存储在本地,不上传到任何服务器
- ✅ **格式验证**:导入时严格验证数据格式,防止损坏
- ✅ **自动备份**:覆盖操作前自动备份
- ✅ **版本控制**:文件包含版本号,便于未来兼容

## 常见问题

### Q: 备份存储在哪里?

A: 存储在浏览器的 `sessionStorage` 中,仅在当前标签页有效,关闭标签页后自动清除。

### Q: 为什么看不到备份文件?

A: 备份是临时存储在内存中的,不会生成文件。如需永久保存,请使用导出功能。

### Q: 可以自动同步吗?

A: 目前需要手动导出/导入。自动同步需要云服务支持,暂不提供以保护隐私。

### Q: 导入后发现配置不对怎么办?

A: 如果标签页未关闭,可以通过控制台恢复备份(参见上方说明)。

### Q: 支持跨浏览器同步吗?

A: 支持!只需在一个浏览器导出,在另一个浏览器导入即可。

## 最佳实践

1. **定期导出**:养成定期导出配置的习惯
2. **云盘备份**:将导出的 JSON 文件保存到云盘
3. **测试导入**:在新设备先用合并模式测试
4. **保留备份**:重要操作前先导出一份备份
5. **版本管理**:可以为不同配置状态保存多个版本

## 技术细节

- **格式版本**: `gemini-voyager.folders.v1`
- **去重策略**: 按 `id` 和 `conversationId` 去重
- **文件编码**: UTF-8
- **最大文件大小**: 理论无限制(受浏览器内存限制)
- **兼容性**: Chrome 88+, Firefox 85+, Safari 14+

## 反馈与支持

如有问题或建议,请访问:
https://github.com/Nagi-ovo/gemini-voyager/issues/36


================================================
FILE: .github/docs/safari/INSTALLATION.md
================================================
# Safari Extension Installation Guide

English | [简体中文](INSTALLATION_ZH.md)

A simple guide for installing Voyager on Safari.

## Requirements

- **macOS 11+**
- **Safari 14+**

## Installation Steps

### 1. Download

Get the latest `gemini-voyager-X.Y.Z.dmg` from [GitHub Releases](https://github.com/Nagi-ovo/gemini-voyager/releases).

### 2. Install

Double-click the `.dmg` file and follow the prompts to install the application.

### 3. Enable in Safari

1. Open **Safari → Settings** (or Preferences)
2. Go to **Extensions** tab
3. Check **Voyager** to enable
4. Visit [Gemini](https://gemini.google.com) to test

Done! 🎉

## Troubleshooting

### Safari doesn't show the extension

1. Safari → Settings → Advanced → Enable "Show Develop menu"
2. Develop → Allow Unsigned Extensions
3. Restart Safari

## For Developers

Want to build from source or contribute? See the [Safari Development Guide](../../../safari/README.md) for:

- Building from source
- Development workflow
- Adding Swift native code
- Advanced debugging

## Uninstall

1. Safari → Settings → Extensions → Uncheck Voyager
2. Delete the app from Applications folder

---

**Need help?** Open an issue on [GitHub](https://github.com/Nagi-ovo/gemini-voyager/issues)


================================================
FILE: .github/docs/safari/INSTALLATION_ZH.md
================================================
# Safari 扩展安装指南

[English](INSTALLATION.md) | 简体中文

在 Safari 上安装 Voyager 的简单指南。

## 系统要求

- **macOS 11+**
- **Safari 14+**

## 安装步骤

### 1. 下载

从 [GitHub Releases](https://github.com/Nagi-ovo/gemini-voyager/releases) 下载最新的 `gemini-voyager-X.Y.Z.dmg`。

### 2. 安装

双击打开 `.dmg` 文件并按提示安装应用。

### 3. 在 Safari 中启用

1. 打开 **Safari → 设置**(或偏好设置)
2. 前往 **扩展** 标签页
3. 勾选 **Voyager** 启用
4. 访问 [Gemini](https://gemini.google.com) 测试

完成!🎉

## 常见问题

### Safari 中看不到扩展

1. Safari → 设置 → 高级 → 勾选"在菜单栏中显示'开发'菜单"
2. 开发 → 允许未签名的扩展
3. 重启 Safari

## 开发者

想从源代码构建或参与开发?查看 [Safari 开发指南](../../../safari/README_ZH.md) 了解:

- 从源代码构建
- 开发工作流
- 添加 Swift 原生代码
- 高级调试

## 卸载

1. Safari → 设置 → 扩展 → 取消勾选 Voyager
2. 从应用程序文件夹删除该应用

---

**需要帮助?** 在 [GitHub](https://github.com/Nagi-ovo/gemini-voyager/issues) 提交 Issue


================================================
FILE: .github/pull_request_template.md
================================================
### 🚫 AI Policy / AI 政策

- **We explicitly reject AI-generated PRs that have not been manually verified.**
- **本项目拒绝接受任何未经人工复核的 AI 生成的 PR。**
- Low-quality AI PRs will be closed immediately. / 低质量的 AI PR 会被直接关闭。
- You must understand and take responsibility for every line of code you submit. / 你必须理解并对你提交的每一行代码负责。
- **Workflow Proficiency / 协作能力**: Ensure you are familiar with GitHub/Git workflows and maintain a clean Git history. Please learn the basics first if needed to avoid messy PR history. / 请确保你熟悉 GitHub/Git 工作流并保持 Git 历史整洁。如有必要请先学习相关知识,避免 PR 历史过于混乱。

---

### Description / 描述

<!-- Explain the goal of this PR and what changes were made. -->
<!-- 请解释此 PR 的目标以及做了哪些更改。 -->

### Related Issue / 相关 Issue

<!-- Link the issue this PR resolves (e.g., Closes #123). FOR NEW FEATURES: You MUST open an Issue for discussion first. PRs submitted without prior discussion will be closed. -->
<!-- 链接此 PR 解决的 issue(例如:Closes #123)。对于新功能:请务必先开启一个 Issue 进行讨论,未经讨论直接提交的 PR 会被关闭。 -->

### Visual Proof / 可视化证据

<!-- REQUIRED for UI chan
Download .txt
gitextract_g57kkec9/

├── .claude/
│   ├── rules/
│   │   ├── content-scripts.md
│   │   ├── high-complexity.md
│   │   ├── i18n.md
│   │   └── typescript.md
│   ├── settings.json
│   └── skills/
│       └── safari-release/
│           └── SKILL.md
├── .editorconfig
├── .entire/
│   ├── .gitignore
│   └── settings.json
├── .gitattributes
├── .github/
│   ├── CONTRIBUTING.md
│   ├── CONTRIBUTING_ES.md
│   ├── CONTRIBUTING_FR.md
│   ├── CONTRIBUTING_JA.md
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   └── feature_request.yml
│   ├── README_AR.md
│   ├── README_ES.md
│   ├── README_FR.md
│   ├── README_JA.md
│   ├── README_KO.md
│   ├── README_PT.md
│   ├── README_RU.md
│   ├── README_ZH.md
│   ├── README_ZH_TW.md
│   ├── RELEASE_TEMPLATE.md
│   ├── docs/
│   │   ├── CHANGELOG.md
│   │   ├── IMPORT_EXPORT_GUIDE.md
│   │   ├── IMPORT_EXPORT_GUIDE_ZH.md
│   │   └── safari/
│   │       ├── INSTALLATION.md
│   │       └── INSTALLATION_ZH.md
│   ├── pull_request_template.md
│   └── workflows/
│       ├── ci.yml
│       ├── deploy-docs.yml
│       ├── issue-claim.yml
│       ├── issue-validator.yml
│       ├── release.yml
│       ├── sponsors.yml
│       └── stale.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── CLAUDE.md
├── CNAME
├── LICENSE
├── README.md
├── commitlint.config.cjs
├── custom-vite-plugins.ts
├── docs/
│   ├── .vitepress/
│   │   ├── config.mts
│   │   └── theme/
│   │       ├── components/
│   │       │   ├── HomeAskAI.vue
│   │       │   ├── HomeReviews.vue
│   │       │   ├── HomeTeaser.vue
│   │       │   ├── README.md
│   │       │   └── SafariDownloadLink.vue
│   │       ├── index.ts
│   │       └── style.css
│   ├── ar/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   ├── en/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   ├── es/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   ├── fr/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   ├── guide/
│   │   ├── batch-delete.md
│   │   ├── cloud-sync.md
│   │   ├── community.md
│   │   ├── context-sync.md
│   │   ├── deep-research.md
│   │   ├── default-model.md
│   │   ├── export.md
│   │   ├── folders.md
│   │   ├── fork.md
│   │   ├── formula-copy.md
│   │   ├── getting-started.md
│   │   ├── input-collapse.md
│   │   ├── installation.md
│   │   ├── markdown-fix.md
│   │   ├── mermaid.md
│   │   ├── nanobanana.md
│   │   ├── prevent-auto-scroll.md
│   │   ├── prompts.md
│   │   ├── quote-reply.md
│   │   ├── recents-hider.md
│   │   ├── settings.md
│   │   ├── sidebar-auto-hide.md
│   │   ├── sidebar.md
│   │   ├── sponsor.md
│   │   ├── tab-title.md
│   │   └── timeline.md
│   ├── index.md
│   ├── ja/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   ├── ko/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   ├── privacy.md
│   ├── pt/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   ├── public/
│   │   └── google79cf501ea29c5eb1.html
│   ├── ru/
│   │   ├── guide/
│   │   │   ├── batch-delete.md
│   │   │   ├── cloud-sync.md
│   │   │   ├── community.md
│   │   │   ├── context-sync.md
│   │   │   ├── deep-research.md
│   │   │   ├── default-model.md
│   │   │   ├── export.md
│   │   │   ├── folders.md
│   │   │   ├── fork.md
│   │   │   ├── formula-copy.md
│   │   │   ├── getting-started.md
│   │   │   ├── input-collapse.md
│   │   │   ├── installation.md
│   │   │   ├── markdown-fix.md
│   │   │   ├── mermaid.md
│   │   │   ├── nanobanana.md
│   │   │   ├── prevent-auto-scroll.md
│   │   │   ├── prompts.md
│   │   │   ├── quote-reply.md
│   │   │   ├── recents-hider.md
│   │   │   ├── settings.md
│   │   │   ├── sidebar-auto-hide.md
│   │   │   ├── sidebar.md
│   │   │   ├── sponsor.md
│   │   │   ├── tab-title.md
│   │   │   └── timeline.md
│   │   ├── index.md
│   │   └── privacy.md
│   └── zh_TW/
│       ├── guide/
│       │   ├── batch-delete.md
│       │   ├── cloud-sync.md
│       │   ├── community.md
│       │   ├── context-sync.md
│       │   ├── deep-research.md
│       │   ├── default-model.md
│       │   ├── export.md
│       │   ├── folders.md
│       │   ├── fork.md
│       │   ├── formula-copy.md
│       │   ├── getting-started.md
│       │   ├── input-collapse.md
│       │   ├── installation.md
│       │   ├── markdown-fix.md
│       │   ├── mermaid.md
│       │   ├── nanobanana.md
│       │   ├── prevent-auto-scroll.md
│       │   ├── prompts.md
│       │   ├── quote-reply.md
│       │   ├── recents-hider.md
│       │   ├── settings.md
│       │   ├── sidebar-auto-hide.md
│       │   ├── sidebar.md
│       │   ├── sponsor.md
│       │   ├── tab-title.md
│       │   └── timeline.md
│       ├── index.md
│       └── privacy.md
├── eslint.config.js
├── manifest.dev.json
├── manifest.json
├── nodemon.chrome.json
├── nodemon.firefox.json
├── nodemon.safari.json
├── package.json
├── public/
│   ├── contentStyle.css
│   ├── fetchInterceptor.js
│   ├── katex-config.js
│   └── prevent-auto-scroll.js
├── safari/
│   ├── App/
│   │   └── SafariWebExtensionHandler.swift
│   ├── Models/
│   │   ├── SafariMessage.swift
│   │   └── voyager-v1.3.6.dmg
│   ├── README.md
│   ├── README_ZH.md
│   └── Resources/
│       └── example-native-messaging.js
├── scripts/
│   ├── build-edge.js
│   ├── build-safari.sh
│   ├── bump-version.js
│   ├── generate-sponsors.cjs
│   └── launch-chrome.cjs
├── sponsorkit/
│   └── sponsors.json
├── src/
│   ├── assets/
│   │   └── styles/
│   │       └── tailwind.css
│   ├── components/
│   │   ├── DarkModeToggle.tsx
│   │   ├── LanguageSwitcher.tsx
│   │   └── ui/
│   │       ├── button.tsx
│   │       ├── card.tsx
│   │       ├── label.tsx
│   │       ├── select.tsx
│   │       ├── slider.tsx
│   │       └── switch.tsx
│   ├── contexts/
│   │   └── LanguageContext.tsx
│   ├── core/
│   │   ├── errors/
│   │   │   └── AppError.ts
│   │   ├── index.ts
│   │   ├── services/
│   │   │   ├── AccountIsolationService.ts
│   │   │   ├── DOMService.ts
│   │   │   ├── DataBackupService.ts
│   │   │   ├── GoogleDriveSyncService.ts
│   │   │   ├── KeyboardShortcutService.ts
│   │   │   ├── LoggerService.ts
│   │   │   ├── StorageMonitor.ts
│   │   │   ├── StorageService.ts
│   │   │   └── __tests__/
│   │   │       ├── AccountIsolationService.test.ts
│   │   │       ├── GoogleDriveSyncService.test.ts
│   │   │       └── StorageService.test.ts
│   │   ├── types/
│   │   │   ├── common.ts
│   │   │   ├── folder.ts
│   │   │   ├── keyboardShortcut.ts
│   │   │   ├── sync.ts
│   │   │   └── timeline.ts
│   │   └── utils/
│   │       ├── __tests__/
│   │       │   ├── browser.test.ts
│   │       │   ├── concurrency.test.ts
│   │       │   ├── extensionContext.test.ts
│   │       │   ├── gemini.test.ts
│   │       │   ├── rtl.test.ts
│   │       │   ├── updateReminder.test.ts
│   │       │   └── version.test.ts
│   │       ├── array.ts
│   │       ├── async.ts
│   │       ├── browser.ts
│   │       ├── concurrency.ts
│   │       ├── extensionContext.ts
│   │       ├── gemini.ts
│   │       ├── hash.ts
│   │       ├── rtl.ts
│   │       ├── safariStorage.ts
│   │       ├── selectors.ts
│   │       ├── storageMigration.ts
│   │       ├── text.ts
│   │       ├── updateReminder.ts
│   │       └── version.ts
│   ├── features/
│   │   ├── backup/
│   │   │   ├── index.ts
│   │   │   ├── services/
│   │   │   │   ├── BackupService.ts
│   │   │   │   └── PromptImportExportService.ts
│   │   │   └── types/
│   │   │       └── backup.ts
│   │   ├── contextSync/
│   │   │   ├── adapters/
│   │   │   │   └── index.ts
│   │   │   ├── services/
│   │   │   │   └── SyncService.ts
│   │   │   └── types.ts
│   │   ├── export/
│   │   │   ├── services/
│   │   │   │   ├── ConversationExportService.ts
│   │   │   │   ├── DOMContentExtractor.ts
│   │   │   │   ├── DeepResearchPDFPrintService.ts
│   │   │   │   ├── ImageExportService.ts
│   │   │   │   ├── ImageRenderService.ts
│   │   │   │   ├── MarkdownFormatter.ts
│   │   │   │   ├── PDFPrintService.ts
│   │   │   │   └── __tests__/
│   │   │   │       ├── ConversationExportService.test.ts
│   │   │   │       ├── DOMContentExtractor.test.ts
│   │   │   │       ├── DeepResearchPDFPrintService.test.ts
│   │   │   │       ├── ImageExportService.test.ts
│   │   │   │       ├── ImageRenderService.test.ts
│   │   │   │       ├── MarkdownFormatter.test.ts
│   │   │   │       ├── PDFPrintService.safari.test.ts
│   │   │   │       └── PDFPrintService.test.ts
│   │   │   ├── types/
│   │   │   │   ├── errors.ts
│   │   │   │   └── export.ts
│   │   │   └── ui/
│   │   │       ├── ExportDialog.ts
│   │   │       ├── ExportErrorMessage.ts
│   │   │       ├── ExportToast.ts
│   │   │       └── __tests__/
│   │   │           ├── ExportDialog.safariHint.test.ts
│   │   │           ├── ExportDialog.test.ts
│   │   │           ├── ExportErrorMessage.test.ts
│   │   │           └── ExportToast.test.ts
│   │   ├── folder/
│   │   │   ├── services/
│   │   │   │   └── FolderImportExportService.ts
│   │   │   └── types/
│   │   │       └── import-export.ts
│   │   └── formulaCopy/
│   │       ├── FormulaCopyService.test.ts
│   │       ├── FormulaCopyService.ts
│   │       └── index.ts
│   ├── global.d.ts
│   ├── hooks/
│   │   ├── useDarkMode.ts
│   │   └── useWidthAdjuster.ts
│   ├── lib/
│   │   └── utils.ts
│   ├── locales/
│   │   ├── ar/
│   │   │   └── messages.json
│   │   ├── en/
│   │   │   └── messages.json
│   │   ├── es/
│   │   │   └── messages.json
│   │   ├── fr/
│   │   │   └── messages.json
│   │   ├── ja/
│   │   │   └── messages.json
│   │   ├── ko/
│   │   │   └── messages.json
│   │   ├── pt/
│   │   │   └── messages.json
│   │   ├── ru/
│   │   │   └── messages.json
│   │   ├── zh/
│   │   │   └── messages.json
│   │   └── zh_TW/
│   │       └── messages.json
│   ├── pages/
│   │   ├── background/
│   │   │   └── index.ts
│   │   ├── content/
│   │   │   ├── changelog/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── changelog.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── notes/
│   │   │   │       ├── 1.2.9.md
│   │   │   │       ├── 1.3.0.md
│   │   │   │       ├── 1.3.1.md
│   │   │   │       ├── 1.3.2.md
│   │   │   │       ├── 1.3.3.md
│   │   │   │       ├── 1.3.4.md
│   │   │   │       ├── 1.3.5.md
│   │   │   │       └── 1.3.6.md
│   │   │   ├── chatWidth/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── chatWidth.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── contextSync/
│   │   │   │   ├── capture.ts
│   │   │   │   └── index.ts
│   │   │   ├── deepResearch/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── menuButton.test.ts
│   │   │   │   │   └── reportExtractor.test.ts
│   │   │   │   ├── download.ts
│   │   │   │   ├── extractor.ts
│   │   │   │   ├── formatter.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── menuButton.ts
│   │   │   │   ├── reportExtractor.ts
│   │   │   │   └── types.ts
│   │   │   ├── defaultModel/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── modelLocker.test.ts
│   │   │   │   ├── modelLocker.ts
│   │   │   │   └── styles.css
│   │   │   ├── editInputWidth/
│   │   │   │   └── index.ts
│   │   │   ├── export/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── conversationDom.test.ts
│   │   │   │   │   ├── conversationMenuI18n.test.ts
│   │   │   │   │   ├── conversationMenuInjection.test.ts
│   │   │   │   │   ├── responseActionImageButton.test.ts
│   │   │   │   │   ├── responseImageCopy.test.ts
│   │   │   │   │   ├── selectionModeInteraction.test.ts
│   │   │   │   │   ├── selectionUtils.test.ts
│   │   │   │   │   ├── sidebarConversationTarget.test.ts
│   │   │   │   │   ├── sidebarExportResume.test.ts
│   │   │   │   │   └── topNodePreload.test.ts
│   │   │   │   ├── conversationDom.ts
│   │   │   │   ├── conversationMenuInjection.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── responseActionImageButton.ts
│   │   │   │   ├── responseImageCopy.ts
│   │   │   │   ├── selectionUtils.ts
│   │   │   │   ├── sidebarConversationTarget.ts
│   │   │   │   ├── sidebarExportResume.ts
│   │   │   │   └── topNodePreload.ts
│   │   │   ├── folder/
│   │   │   │   ├── README.md
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── aistudio.test.ts
│   │   │   │   │   ├── conversationSort.test.ts
│   │   │   │   │   ├── folderNameInteraction.test.ts
│   │   │   │   │   ├── moveToFolderMenuItem.test.ts
│   │   │   │   │   └── treeIndent.test.ts
│   │   │   │   ├── aistudio.ts
│   │   │   │   ├── conversationSort.ts
│   │   │   │   ├── folderColors.ts
│   │   │   │   ├── gemConfig.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── manager.ts
│   │   │   │   ├── moveToFolderMenuItem.ts
│   │   │   │   ├── storage/
│   │   │   │   │   └── FolderStorageAdapter.ts
│   │   │   │   └── types.ts
│   │   │   ├── folderSpacing/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── folderSpacing.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── fork/
│   │   │   │   ├── ForkNodesService.ts
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── ForkNodesService.test.ts
│   │   │   │   │   ├── branching.test.ts
│   │   │   │   │   ├── chatPairs.test.ts
│   │   │   │   │   ├── featureFlag.test.ts
│   │   │   │   │   ├── forkContext.test.ts
│   │   │   │   │   ├── index.test.ts
│   │   │   │   │   ├── markdown.test.ts
│   │   │   │   │   └── turnId.test.ts
│   │   │   │   ├── branching.ts
│   │   │   │   ├── chatPairs.ts
│   │   │   │   ├── featureFlag.ts
│   │   │   │   ├── forkContext.ts
│   │   │   │   ├── forkTypes.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── markdown.ts
│   │   │   │   └── turnId.ts
│   │   │   ├── gemsHider/
│   │   │   │   └── index.ts
│   │   │   ├── index.tsx
│   │   │   ├── inputCollapse/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── inputCollapse.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── katexConfig/
│   │   │   │   └── index.ts
│   │   │   ├── markdownPatcher/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── fixBrokenBoldTags.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── mermaid/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── mermaid.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── preventAutoScroll/
│   │   │   │   └── index.ts
│   │   │   ├── prompt/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── scrollHint.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── scrollHint.ts
│   │   │   ├── quoteReply/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── quoteReply.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── recentsHider/
│   │   │   │   └── index.ts
│   │   │   ├── sendBehavior/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── sendBehavior.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── utils.ts
│   │   │   ├── shared/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── nativeMenuItemTemplate.test.ts
│   │   │   │   └── nativeMenuItemTemplate.ts
│   │   │   ├── sidebarAutoHide/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── SidebarAutoHide.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── sidebarWidth/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── sidebarWidthCentering.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── timeline/
│   │   │   │   ├── EventBus.ts
│   │   │   │   ├── StarredMessagesService.ts
│   │   │   │   ├── TimelinePreviewPanel.ts
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── TimelineBootstrap.test.ts
│   │   │   │   │   ├── TimelineManagerActiveIndex.test.ts
│   │   │   │   │   ├── TimelineManagerFlowClickActiveReset.test.ts
│   │   │   │   │   ├── TimelineManagerNavigationRefresh.test.ts
│   │   │   │   │   ├── TimelineManagerPreviewPanelReposition.test.ts
│   │   │   │   │   ├── TimelineManagerSelectorPriority.test.ts
│   │   │   │   │   ├── TimelineManagerSummaryExtraction.test.ts
│   │   │   │   │   ├── TimelineManagerTooltipDirection.test.ts
│   │   │   │   │   └── TimelinePreviewPanel.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── manager.ts
│   │   │   │   ├── starredTypes.ts
│   │   │   │   └── types.ts
│   │   │   ├── timestamp/
│   │   │   │   ├── TimestampService.ts
│   │   │   │   └── __tests__/
│   │   │   │       └── TimestampService.test.ts
│   │   │   ├── titleUpdater/
│   │   │   │   └── index.ts
│   │   │   ├── visualEffects/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── rain.test.ts
│   │   │   │   │   ├── sakura.test.ts
│   │   │   │   │   └── snow.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── rain.ts
│   │   │   │   ├── sakura.ts
│   │   │   │   └── snow.ts
│   │   │   └── watermarkRemover/
│   │   │       ├── __tests__/
│   │   │       │   ├── downloadButton.test.ts
│   │   │       │   ├── downloadToasts.test.ts
│   │   │       │   ├── fetchInterceptor.test.ts
│   │   │       │   └── statusToast.test.ts
│   │   │       ├── alphaMap.ts
│   │   │       ├── blendModes.ts
│   │   │       ├── credits.ts
│   │   │       ├── downloadButton.ts
│   │   │       ├── index.ts
│   │   │       ├── statusToast.ts
│   │   │       └── watermarkEngine.ts
│   │   ├── devtools/
│   │   │   ├── index.html
│   │   │   └── index.ts
│   │   ├── options/
│   │   │   ├── Options.css
│   │   │   ├── Options.tsx
│   │   │   ├── index.css
│   │   │   ├── index.html
│   │   │   └── index.tsx
│   │   ├── panel/
│   │   │   ├── Panel.css
│   │   │   ├── Panel.tsx
│   │   │   ├── index.css
│   │   │   ├── index.html
│   │   │   └── index.tsx
│   │   └── popup/
│   │       ├── Popup.tsx
│   │       ├── __tests__/
│   │       │   └── latestVersion.test.ts
│   │       ├── components/
│   │       │   ├── CloudSyncSettings.tsx
│   │       │   ├── ContextSyncSettings.tsx
│   │       │   ├── KeyboardShortcutSettings.tsx
│   │       │   ├── StarredHistory.tsx
│   │       │   ├── WebsiteLogos.tsx
│   │       │   ├── WidthSlider.tsx
│   │       │   └── __tests__/
│   │       │       └── CloudSyncSettings.test.tsx
│   │       ├── index.css
│   │       ├── index.html
│   │       ├── index.tsx
│   │       └── utils/
│   │           └── latestVersion.ts
│   ├── tests/
│   │   └── setup.ts
│   ├── utils/
│   │   ├── __tests__/
│   │   │   ├── i18n.test.ts
│   │   │   ├── language.test.ts
│   │   │   ├── mergeForkNodes.test.ts
│   │   │   ├── mergeStarredMessages.test.ts
│   │   │   └── translations.test.ts
│   │   ├── i18n.ts
│   │   ├── language.ts
│   │   ├── localeMessages.ts
│   │   ├── merge.test.ts
│   │   ├── merge.ts
│   │   └── translations.ts
│   └── vite-env.d.ts
├── tsconfig.json
├── vite.config.base.ts
├── vite.config.chrome.ts
├── vite.config.firefox.ts
├── vite.config.safari.ts
└── vitest.config.ts
Download .txt
Showing preview only (203K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (2132 symbols across 201 files)

FILE: custom-vite-plugins.ts
  constant FIREFOX_OUT_DIR_MARKER (line 6) | const FIREFOX_OUT_DIR_MARKER = 'dist_firefox';
  constant CHANGELOG_PROMO_BANNERS (line 7) | const CHANGELOG_PROMO_BANNERS = [
  function stripDevIcons (line 14) | function stripDevIcons(isDev: boolean) {
  type LocaleMessages (line 58) | type LocaleMessages = Record<string, { message: string; description?: st...
  function stripDescriptions (line 60) | function stripDescriptions(raw: LocaleMessages): LocaleMessages {
  function stripI18nDescriptions (line 66) | function stripI18nDescriptions(isDev: boolean): PluginOption {
  function crxI18n (line 81) | function crxI18n(options: {

FILE: docs/.vitepress/theme/index.ts
  method enhanceApp (line 21) | enhanceApp({ app }) {

FILE: public/prevent-auto-scroll.js
  function isEnabled (line 9) | function isEnabled() {
  function getScrollTop (line 14) | function getScrollTop(el) {
  function getScrollHeight (line 19) | function getScrollHeight(el) {
  function getClientHeight (line 24) | function getClientHeight(el) {
  function isScrolledUp (line 29) | function isScrolledUp(el) {
  function isScrollingDownTo (line 38) | function isScrollingDownTo(el, args) {
  function isScrollingDownBy (line 52) | function isScrollingDownBy(args) {
  function shouldBlockScrollTo (line 62) | function shouldBlockScrollTo(el, args) {
  function shouldBlockScrollBy (line 70) | function shouldBlockScrollBy(el, args) {

FILE: safari/Resources/example-native-messaging.js
  function sendNativeMessage (line 17) | async function sendNativeMessage(action, payload = {}) {
  function checkNativeHealth (line 34) | async function checkNativeHealth() {
  function getExtensionVersion (line 45) | async function getExtensionVersion() {
  function syncStorage (line 57) | async function syncStorage(data) {

FILE: scripts/build-edge.js
  constant EDGE_INCOMPATIBLE_FIELDS (line 18) | const EDGE_INCOMPATIBLE_FIELDS = ['key'];
  function buildForEdge (line 20) | async function buildForEdge() {

FILE: scripts/bump-version.js
  function readJson (line 15) | function readJson(filePath) {
  function writeJson (line 20) | function writeJson(filePath, data) {
  function bumpVersion (line 25) | function bumpVersion(version) {
  function main (line 43) | async function main() {

FILE: scripts/generate-sponsors.cjs
  constant ROOT (line 17) | const ROOT = path.resolve(__dirname, '..');
  constant FRIENDS_PATH (line 18) | const FRIENDS_PATH = path.join(ROOT, 'sponsorkit', 'sponsors.json');
  constant OUTPUT_DIR (line 19) | const OUTPUT_DIR = path.join(ROOT, 'docs', 'public', 'assets');
  constant OUTPUT_PATH (line 20) | const OUTPUT_PATH = path.join(OUTPUT_DIR, 'sponsors.svg');
  constant OUTPUT_PNG_PATH (line 21) | const OUTPUT_PNG_PATH = path.join(OUTPUT_DIR, 'sponsors.png');
  constant SPONSORS_URL (line 22) | const SPONSORS_URL = 'https://github.com/sponsors/Nagi-ovo';
  constant OWNER_LOGIN (line 23) | const OWNER_LOGIN = 'Nagi-ovo';
  constant GRAPHQL_ENDPOINT (line 24) | const GRAPHQL_ENDPOINT = 'https://api.github.com/graphql';
  constant AFDIAN_API (line 25) | const AFDIAN_API = 'https://afdian.com/api/open/query-sponsor';
  constant FONT_FAMILY (line 26) | const FONT_FAMILY =
  function main (line 29) | async function main() {
  function loadFriends (line 49) | async function loadFriends() {
  function ensureDir (line 68) | async function ensureDir(target) {
  function fetchGitHubSponsors (line 72) | async function fetchGitHubSponsors(token) {
  function fetchAfdianSponsors (line 176) | async function fetchAfdianSponsors(userId, token) {
  function buildSvg (line 239) | async function buildSvg({ githubSponsors, afdianSponsors, friends }) {
  function avatarDataUri (line 303) | async function avatarDataUri(url) {
  function embedAvatarData (line 316) | async function embedAvatarData(sponsors) {
  function renderSponsorGrid (line 325) | function renderSponsorGrid({ sponsors, title, x, y, width, sponsorUrl }) {
  function renderFriendTable (line 399) | function renderFriendTable({ friends, x, y, width }) {
  function escapeText (line 431) | function escapeText(value) {
  function escapeAttr (line 435) | function escapeAttr(value) {

FILE: scripts/launch-chrome.cjs
  constant TARGET_URL (line 9) | const TARGET_URL = process.env.CHROME_OPEN_URL || 'https://gemini.google...
  constant BUILD_TIMEOUT_MS (line 10) | const BUILD_TIMEOUT_MS = Number(process.env.CHROME_OPEN_BUILD_TIMEOUT_MS...
  constant BUILD_POLL_INTERVAL_MS (line 11) | const BUILD_POLL_INTERVAL_MS = Number(process.env.CHROME_OPEN_POLL_INTER...
  function main (line 31) | async function main() {
  function startDevBuild (line 45) | function startDevBuild() {
  function launchChrome (line 57) | async function launchChrome() {
  function attachProcessHandlers (line 76) | function attachProcessHandlers() {
  function waitForManifest (line 96) | async function waitForManifest(startTime) {
  function startReloadWatcher (line 108) | function startReloadWatcher() {
  function manifestMtime (line 123) | function manifestMtime() {
  function reloadTargetTabs (line 131) | async function reloadTargetTabs() {
  function fetchJson (line 148) | async function fetchJson(url) {
  function sendDevtoolsCommand (line 154) | function sendDevtoolsCommand(url, method, params) {
  function getAvailablePort (line 190) | function getAvailablePort() {
  function log (line 204) | function log(message) {

FILE: src/components/ui/button.tsx
  type ButtonProps (line 38) | interface ButtonProps

FILE: src/components/ui/select.tsx
  type SelectProps (line 5) | interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectEleme...

FILE: src/components/ui/slider.tsx
  type SliderProps (line 5) | interface SliderProps extends Omit<React.InputHTMLAttributes<HTMLInputEl...

FILE: src/components/ui/switch.tsx
  type SwitchProps (line 5) | interface SwitchProps extends Omit<React.InputHTMLAttributes<HTMLInputEl...

FILE: src/contexts/LanguageContext.tsx
  type LanguageContextType (line 10) | interface LanguageContextType {

FILE: src/core/errors/AppError.ts
  type ErrorCode (line 6) | enum ErrorCode {
  type ErrorContext (line 35) | interface ErrorContext {
  class AppError (line 39) | class AppError extends Error {
    method constructor (line 40) | constructor(
    method toJSON (line 55) | toJSON(): Record<string, unknown> {
  class StorageError (line 67) | class StorageError extends AppError {
    method constructor (line 68) | constructor(code: ErrorCode, message: string, context?: ErrorContext, ...
  class DOMError (line 74) | class DOMError extends AppError {
    method constructor (line 75) | constructor(code: ErrorCode, message: string, context?: ErrorContext, ...
  class ValidationError (line 81) | class ValidationError extends AppError {
    method constructor (line 82) | constructor(message: string, context?: ErrorContext) {
  class ErrorHandler (line 91) | class ErrorHandler {
    method handle (line 92) | static handle(error: unknown, context?: ErrorContext): AppError {
    method isRecoverable (line 104) | static isRecoverable(error: AppError): boolean {

FILE: src/core/services/AccountIsolationService.ts
  constant EMAIL_PATTERN (line 4) | const EMAIL_PATTERN = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i;
  constant PROFILE_MAP_VERSION (line 5) | const PROFILE_MAP_VERSION = 1;
  constant ACCOUNT_ISOLATION_KEY_BY_PLATFORM (line 6) | const ACCOUNT_ISOLATION_KEY_BY_PLATFORM = {
  type AccountProfileRecord (line 11) | interface AccountProfileRecord {
  type AccountProfileMap (line 19) | interface AccountProfileMap {
  type AccountScope (line 27) | interface AccountScope {
  type AccountScopeHints (line 34) | interface AccountScopeHints {
  type AccountContext (line 40) | interface AccountContext {
  type AccountPlatform (line 45) | type AccountPlatform = 'gemini' | 'aistudio';
  function parseHostname (line 47) | function parseHostname(url: string): string | null {
  function isAIStudioHost (line 55) | function isAIStudioHost(hostname: string | null): boolean {
  function isGeminiHost (line 59) | function isGeminiHost(hostname: string | null): boolean {
  function detectAccountPlatformFromUrl (line 63) | function detectAccountPlatformFromUrl(pageUrl: string | null | undefined...
  function getAccountIsolationStorageKey (line 69) | function getAccountIsolationStorageKey(platform: AccountPlatform): string {
  function isRecord (line 73) | function isRecord(value: unknown): value is Record<string, unknown> {
  function toStringRecord (line 77) | function toStringRecord(value: unknown): Record<string, string> {
  function toProfileRecord (line 87) | function toProfileRecord(value: unknown): AccountProfileRecord | null {
  function createDefaultProfileMap (line 107) | function createDefaultProfileMap(): AccountProfileMap {
  function parseProfileMap (line 117) | function parseProfileMap(value: unknown): AccountProfileMap {
  function extractRouteUserIdFromPath (line 141) | function extractRouteUserIdFromPath(pathname: string): string | null {
  function extractRouteUserIdFromUrl (line 146) | function extractRouteUserIdFromUrl(url: string): string | null {
  function normalizeEmailAddress (line 155) | function normalizeEmailAddress(email: string | null | undefined): string...
  function extractEmailFromText (line 162) | function extractEmailFromText(text: string | null | undefined): string |...
  function extractEmailFromElement (line 169) | function extractEmailFromElement(element: Element): string | null {
  function findEmailBySelectors (line 189) | function findEmailBySelectors(
  function detectAccountContextFromDocument (line 205) | function detectAccountContextFromDocument(pageUrl: string, doc: Document...
  function buildScopedStorageKey (line 247) | function buildScopedStorageKey(baseKey: string, accountKey: string): str...
  function buildScopedFolderStorageKey (line 251) | function buildScopedFolderStorageKey(accountKey: string): string {
  class AccountIsolationService (line 255) | class AccountIsolationService {
    method serialize (line 258) | private serialize<T>(operation: () => Promise<T>): Promise<T> {
    method isIsolationEnabled (line 264) | async isIsolationEnabled(options?: {
    method resolveAccountScope (line 291) | async resolveAccountScope(hints: AccountScopeHints = {}): Promise<Acco...
    method readProfileMap (line 347) | private async readProfileMap(): Promise<AccountProfileMap> {
    method writeProfileMap (line 356) | private async writeProfileMap(map: AccountProfileMap): Promise<void> {

FILE: src/core/services/DOMService.ts
  type WaitForElementOptions (line 9) | interface WaitForElementOptions {
  class DOMService (line 15) | class DOMService {
    method waitForElement (line 22) | async waitForElement(
    method querySelector (line 80) | querySelector<T extends HTMLElement = HTMLElement>(
    method querySelectorAll (line 115) | querySelectorAll<T extends HTMLElement = HTMLElement>(
    method createElement (line 142) | createElement<K extends keyof HTMLElementTagNameMap>(
    method observeElement (line 173) | observeElement(
    method disconnectObserver (line 193) | disconnectObserver(observerId: string): void {
    method disconnectAllObservers (line 206) | disconnectAllObservers(): void {
    method getComputedStyleValue (line 216) | getComputedStyleValue(element: Element, property: string): string {
    method matches (line 223) | matches(element: Element, selector: string): boolean {
    method closest (line 230) | closest<T extends HTMLElement = HTMLElement>(element: Element, selecto...

FILE: src/core/services/DataBackupService.ts
  type BackupMetadata (line 20) | interface BackupMetadata {
  type BackupData (line 27) | interface BackupData<T> {
  class DataBackupService (line 32) | class DataBackupService<T = unknown> {
    method constructor (line 40) | constructor(
    method createPrimaryBackup (line 53) | createPrimaryBackup(data: T): boolean {
    method createEmergencyBackup (line 69) | createEmergencyBackup(data: T): boolean {
    method createBeforeUnloadBackup (line 84) | private createBeforeUnloadBackup(data: T): boolean {
    method setupBeforeUnloadBackup (line 102) | setupBeforeUnloadBackup(getDataFn: () => T): void {
    method recoverFromBackup (line 123) | recoverFromBackup(): T | null {
    method loadBackup (line 145) | private loadBackup(key: string, type: string): T | null {
    method createBackupData (line 180) | private createBackupData(data: T): BackupData<T> {
    method isBackupValid (line 198) | private isBackupValid(backup: BackupData<T>): boolean {
    method getItemCount (line 224) | private getItemCount(data: T): number {
    method updateMetadata (line 244) | private updateMetadata(type: string, metadata: BackupMetadata): void {
    method getAllMetadata (line 257) | getAllMetadata(): Record<string, BackupMetadata> {
    method clearAllBackups (line 269) | clearAllBackups(): void {
    method destroy (line 284) | destroy(): void {

FILE: src/core/services/GoogleDriveSyncService.ts
  constant FOLDERS_FILE_NAME (line 30) | const FOLDERS_FILE_NAME = 'gemini-voyager-folders.json';
  constant AISTUDIO_FOLDERS_FILE_NAME (line 31) | const AISTUDIO_FOLDERS_FILE_NAME = 'gemini-voyager-aistudio-folders.json';
  constant PROMPTS_FILE_NAME (line 32) | const PROMPTS_FILE_NAME = 'gemini-voyager-prompts.json';
  constant STARRED_FILE_NAME (line 33) | const STARRED_FILE_NAME = 'gemini-voyager-starred.json';
  constant FORKS_FILE_NAME (line 34) | const FORKS_FILE_NAME = 'gemini-voyager-forks.json';
  constant BACKUP_FOLDER_NAME (line 35) | const BACKUP_FOLDER_NAME = 'Gemini Voyager Data';
  constant DRIVE_API_BASE (line 36) | const DRIVE_API_BASE = 'https://www.googleapis.com/drive/v3';
  constant DRIVE_UPLOAD_BASE (line 37) | const DRIVE_UPLOAD_BASE = 'https://www.googleapis.com/upload/drive/v3';
  constant MAX_RETRIES (line 40) | const MAX_RETRIES = 3;
  constant INITIAL_RETRY_DELAY_MS (line 41) | const INITIAL_RETRY_DELAY_MS = 1000;
  constant IDENTITY_TOKEN_TTL_SECONDS (line 42) | const IDENTITY_TOKEN_TTL_SECONDS = 55 * 60;
  class GoogleDriveSyncService (line 48) | class GoogleDriveSyncService {
    method constructor (line 62) | constructor() {
    method onStateChange (line 66) | onStateChange(callback: (state: SyncState) => void): void {
    method getState (line 73) | async getState(): Promise<SyncState> {
    method setMode (line 80) | async setMode(mode: SyncMode): Promise<void> {
    method authenticate (line 86) | async authenticate(interactive: boolean = true): Promise<boolean> {
    method signOut (line 108) | async signOut(): Promise<void> {
    method upload (line 136) | async upload(
    method download (line 268) | async download(
    method loadCachedToken (line 357) | private async loadCachedToken(): Promise<void> {
    method saveToken (line 370) | private async saveToken(token: string, expiresIn: number): Promise<voi...
    method clearToken (line 380) | private async clearToken(): Promise<void> {
    method isUserDeniedAuthError (line 390) | private isUserDeniedAuthError(message: string): boolean {
    method extractIdentityToken (line 399) | private extractIdentityToken(result: unknown): string | null {
    method requestIdentityAuthToken (line 414) | private async requestIdentityAuthToken(
    method getTokenFromIdentity (line 451) | private async getTokenFromIdentity(
    method removeCachedAuthToken (line 470) | private async removeCachedAuthToken(token: string): Promise<void> {
    method getTokenFromLegacyWebAuthFlow (line 481) | private async getTokenFromLegacyWebAuthFlow(): Promise<string | null> {
    method getAuthToken (line 531) | private async getAuthToken(interactive: boolean): Promise<string | nul...
    method findFile (line 571) | private async findFile(token: string, fileName: string): Promise<strin...
    method getFileNameForScope (line 582) | private getFileNameForScope(baseFileName: string, accountScope: SyncAc...
    method findFileForScope (line 593) | private async findFileForScope(
    method ensureFileId (line 610) | private async ensureFileId(
    method setFileIdForType (line 659) | private setFileIdForType(
    method ensureBackupFolder (line 682) | private async ensureBackupFolder(token: string): Promise<string> {
    method getFileParents (line 726) | private async getFileParents(token: string, fileId: string): Promise<s...
    method moveFile (line 746) | private async moveFile(
    method checkFileExists (line 764) | private async checkFileExists(token: string, fileId: string): Promise<...
    method createFile (line 775) | private async createFile(token: string, fileName: string, parentId?: s...
    method uploadFileWithRetry (line 796) | private async uploadFileWithRetry(token: string, fileId: string, data:...
    method downloadFileWithRetry (line 818) | private async downloadFileWithRetry<T>(token: string, fileId: string):...
    method loadState (line 838) | private async loadState(): Promise<void> {
    method saveState (line 865) | private async saveState(): Promise<void> {
    method updateState (line 880) | private updateState(partial: Partial<SyncState>): void {
    method notifyStateChange (line 885) | private notifyStateChange(): void {
    method sleep (line 891) | private sleep(ms: number): Promise<void> {

FILE: src/core/services/KeyboardShortcutService.ts
  constant DEFAULT_SHORTCUTS (line 30) | const DEFAULT_SHORTCUTS: KeyboardShortcutConfig = {
  type ShortcutCallback (line 46) | type ShortcutCallback = (action: ShortcutAction, event: KeyboardEvent) =...
  class KeyboardShortcutService (line 52) | class KeyboardShortcutService {
    method constructor (line 63) | private constructor() {
    method getInstance (line 70) | static getInstance(): KeyboardShortcutService {
    method init (line 80) | async init(): Promise<void> {
    method loadConfig (line 89) | private async loadConfig(): Promise<void> {
    method saveConfig (line 124) | async saveConfig(config: KeyboardShortcutConfig, enabled: boolean = th...
    method validateConfig (line 152) | private validateConfig(config: KeyboardShortcutConfig): boolean {
    method isValidShortcut (line 168) | private isValidShortcut(shortcut: KeyboardShortcut): boolean {
    method attachKeyboardListener (line 182) | private attachKeyboardListener(): void {
    method isTypingInInputField (line 206) | private isTypingInInputField(event: KeyboardEvent): boolean {
    method attachStorageListener (line 220) | private attachStorageListener(): void {
    method matchShortcut (line 244) | private matchShortcut(event: KeyboardEvent): ShortcutMatch | null {
    method isShortcutPressed (line 263) | private isShortcutPressed(event: KeyboardEvent, shortcut: KeyboardShor...
    method notifyListeners (line 284) | private notifyListeners(action: ShortcutAction, event: KeyboardEvent):...
    method on (line 297) | on(callback: ShortcutCallback): () => void {
    method off (line 306) | off(callback: ShortcutCallback): void {
    method getConfig (line 313) | getConfig(): { config: KeyboardShortcutConfig; enabled: boolean } {
    method resetToDefaults (line 323) | async resetToDefaults(): Promise<void> {
    method setEnabled (line 330) | async setEnabled(enabled: boolean): Promise<void> {
    method formatShortcut (line 338) | formatShortcut(shortcut: KeyboardShortcut): string {
    method destroy (line 373) | destroy(): void {

FILE: src/core/services/LoggerService.ts
  type LogLevel (line 7) | enum LogLevel {
  type LoggerConfig (line 15) | interface LoggerConfig {
  class LoggerService (line 22) | class LoggerService implements ILogger {
    method constructor (line 26) | private constructor(config: Partial<LoggerConfig> = {}) {
    method getInstance (line 36) | static getInstance(config?: Partial<LoggerConfig>): LoggerService {
    method createChild (line 46) | createChild(prefix: string): ILogger {
    method debug (line 53) | debug(message: string, context?: Record<string, unknown>): void {
    method info (line 57) | info(message: string, context?: Record<string, unknown>): void {
    method warn (line 61) | warn(message: string, context?: Record<string, unknown>): void {
    method error (line 65) | error(message: string, context?: Record<string, unknown>): void {
    method log (line 69) | private log(level: LogLevel, message: string, context?: Record<string,...
    method getLogFunction (line 92) | private getLogFunction(level: LogLevel): (...args: unknown[]) => void {
    method setLevel (line 107) | setLevel(level: LogLevel): void {
    method getLevel (line 111) | getLevel(): LogLevel {

FILE: src/core/services/StorageMonitor.ts
  type StorageQuotaInfo (line 15) | interface StorageQuotaInfo {
  type StorageMonitorConfig (line 23) | interface StorageMonitorConfig {
  type NotificationCallback (line 30) | type NotificationCallback = (message: string, level: 'info' | 'warning' ...
  class StorageMonitor (line 32) | class StorageMonitor {
    method constructor (line 47) | private constructor(config?: Partial<StorageMonitorConfig>) {
    method getInstance (line 57) | static getInstance(config?: Partial<StorageMonitorConfig>): StorageMon...
    method setNotificationCallback (line 67) | setNotificationCallback(callback: NotificationCallback): void {
    method isStorageApiAvailable (line 74) | static isStorageApiAvailable(): boolean {
    method checkQuota (line 85) | async checkQuota(): Promise<StorageQuotaInfo | null> {
    method checkAndWarn (line 121) | async checkAndWarn(): Promise<StorageQuotaInfo | null> {
    method formatWarningMessage (line 158) | private formatWarningMessage(info: StorageQuotaInfo): string {
    method getWarningLevel (line 172) | private getWarningLevel(usagePercent: number): 'info' | 'warning' | 'e...
    method showNotification (line 181) | private showNotification(message: string, level: 'info' | 'warning' | ...
    method showDefaultNotification (line 194) | private showDefaultNotification(message: string, level: 'info' | 'warn...
    method startMonitoring (line 241) | startMonitoring(): void {
    method stopMonitoring (line 277) | stopMonitoring(): void {
    method updateConfig (line 289) | updateConfig(config: Partial<StorageMonitorConfig>): void {
    method getConfig (line 306) | getConfig(): StorageMonitorConfig {
    method getFormattedInfo (line 313) | async getFormattedInfo(): Promise<string | null> {
    method destroy (line 327) | destroy(): void {
    method resetInstance (line 336) | static resetInstance(): void {
  function getStorageMonitor (line 347) | function getStorageMonitor(config?: Partial<StorageMonitorConfig>): Stor...

FILE: src/core/services/StorageService.ts
  type IStorageService (line 14) | interface IStorageService {
  method logger (line 28) | protected get logger(): ReturnType<typeof logger.createChild> {
  method isContextInvalidated (line 33) | private isContextInvalidated(error: unknown): boolean {
  method get (line 37) | async get<T>(key: StorageKey): Promise<Result<T>> {
  method set (line 89) | async set<T>(key: StorageKey, value: T): Promise<Result<void>> {
  method remove (line 131) | async remove(key: StorageKey): Promise<Result<void>> {
  method clear (line 173) | async clear(): Promise<Result<void>> {
  class ChromeStorageService (line 220) | class ChromeStorageService extends BaseChromeStorageService {
  class ChromeLocalStorageService (line 229) | class ChromeLocalStorageService extends BaseChromeStorageService {
  class LocalStorageService (line 237) | class LocalStorageService implements IStorageService {
    method get (line 240) | async get<T>(key: StorageKey): Promise<Result<T>> {
    method set (line 272) | async set<T>(key: StorageKey, value: T): Promise<Result<void>> {
    method remove (line 295) | async remove(key: StorageKey): Promise<Result<void>> {
    method clear (line 317) | async clear(): Promise<Result<void>> {
  type StorageType (line 343) | type StorageType = 'sync' | 'local';
  class StorageFactory (line 348) | class StorageFactory {
    method create (line 353) | static create(type: StorageType = 'sync'): IStorageService {

FILE: src/core/services/__tests__/AccountIsolationService.test.ts
  type MockedChrome (line 13) | type MockedChrome = typeof chrome;
  function createChromeMock (line 15) | function createChromeMock(syncSeed: Record<string, unknown> = {}): Mocke...

FILE: src/core/services/__tests__/GoogleDriveSyncService.test.ts
  type MockedChrome (line 3) | type MockedChrome = typeof chrome;
  function createChromeMock (line 5) | function createChromeMock(): MockedChrome {
  function loadServiceClass (line 53) | async function loadServiceClass() {

FILE: src/core/types/common.ts
  type Result (line 6) | type Result<T, E = Error> = { success: true; data: T } | { success: fals...
  type IDisposable (line 8) | interface IDisposable {
  type ILogger (line 12) | interface ILogger {
  type Nullable (line 19) | type Nullable<T> = T | null;
  type Optional (line 20) | type Optional<T> = T | undefined;
  type Maybe (line 21) | type Maybe<T> = T | null | undefined;
  type Brand (line 26) | type Brand<K, T> = K & { __brand: T };
  type ConversationId (line 28) | type ConversationId = Brand<string, 'ConversationId'>;
  type FolderId (line 29) | type FolderId = Brand<string, 'FolderId'>;
  type TurnId (line 30) | type TurnId = Brand<string, 'TurnId'>;
  type StorageKey (line 113) | type StorageKey = (typeof StorageKeys)[keyof typeof StorageKeys];

FILE: src/core/types/folder.ts
  type Folder (line 7) | interface Folder {
  type ConversationReference (line 19) | interface ConversationReference {
  type FolderData (line 33) | interface FolderData {
  type DragDataType (line 38) | type DragDataType = 'conversation' | 'folder';
  type BaseDragData (line 40) | interface BaseDragData {
  type ConversationDragData (line 45) | interface ConversationDragData extends BaseDragData {
  type FolderDragData (line 54) | interface FolderDragData extends BaseDragData {
  type DragData (line 59) | type DragData = ConversationDragData | FolderDragData;
  type GemConfig (line 61) | interface GemConfig {

FILE: src/core/types/keyboardShortcut.ts
  type ModifierKey (line 14) | type ModifierKey = 'Alt' | 'Ctrl' | 'Shift' | 'Meta';
  type ShortcutKey (line 20) | type ShortcutKey = string;
  type ShortcutAction (line 25) | type ShortcutAction = 'timeline:previous' | 'timeline:next';
  type KeyboardShortcut (line 30) | interface KeyboardShortcut {
  type ShortcutMatch (line 39) | interface ShortcutMatch {
  type KeyboardShortcutConfig (line 47) | interface KeyboardShortcutConfig {
  type KeyboardShortcutStorage (line 55) | interface KeyboardShortcutStorage {

FILE: src/core/types/sync.ts
  type SyncMode (line 15) | type SyncMode = 'disabled' | 'manual' | 'auto';
  type SyncPlatform (line 22) | type SyncPlatform = 'gemini' | 'aistudio';
  type SyncAccountScope (line 24) | interface SyncAccountScope {
  type SyncState (line 33) | interface SyncState {
  type PromptItem (line 55) | interface PromptItem {
  type FolderExportPayload (line 66) | interface FolderExportPayload {
  type PromptExportPayload (line 76) | interface PromptExportPayload {
  type StarredExportPayload (line 94) | interface StarredExportPayload {
  type ForkExportPayload (line 112) | interface ForkExportPayload {
  type SyncData (line 123) | interface SyncData {
  constant DEFAULT_SYNC_STATE (line 148) | const DEFAULT_SYNC_STATE: SyncState = {
  type SyncMessageType (line 162) | type SyncMessageType =
  type SyncMessage (line 173) | interface SyncMessage {
  type SyncResponse (line 187) | interface SyncResponse {

FILE: src/core/types/timeline.ts
  type ScrollMode (line 7) | type ScrollMode = 'jump' | 'flow';
  type SpringProfile (line 9) | type SpringProfile = 'ios' | 'snappy' | 'gentle';
  type TimelineMarker (line 11) | interface TimelineMarker {
  type DotElement (line 22) | interface DotElement extends HTMLButtonElement {
  type TimelineConfig (line 28) | interface TimelineConfig {
  type TimelineUIElements (line 39) | interface TimelineUIElements {
  type VisibleRange (line 48) | interface VisibleRange {
  type ScrollSyncState (line 53) | interface ScrollSyncState {
  type TooltipState (line 62) | interface TooltipState {
  type SliderState (line 69) | interface SliderState {

FILE: src/core/utils/__tests__/rtl.test.ts
  constant ORIGINAL_URL (line 5) | const ORIGINAL_URL = window.location.href;
  function setTestUrl (line 7) | function setTestUrl(pathAndQuery: string): void {

FILE: src/core/utils/array.ts
  function filterTopLevel (line 8) | function filterTopLevel<T extends Element>(elements: T[]): T[] {
  function deduplicateBy (line 36) | function deduplicateBy<T>(items: T[], keyFn: (item: T) => string): T[] {
  function lowerBound (line 55) | function lowerBound(arr: number[], target: number): number {
  function upperBound (line 75) | function upperBound(arr: number[], target: number): number {
  function chunk (line 95) | function chunk<T>(array: T[], size: number): T[][] {
  function sortFolders (line 108) | function sortFolders<T extends { name: string; pinned?: boolean }>(folde...

FILE: src/core/utils/async.ts
  function debounce (line 8) | function debounce<T extends (...args: Parameters<T>) => void>(
  function throttle (line 29) | function throttle<T extends (...args: Parameters<T>) => void>(
  function retry (line 48) | async function retry<T>(
  function sleep (line 81) | function sleep(ms: number): Promise<void> {
  function withTimeout (line 88) | async function withTimeout<T>(

FILE: src/core/utils/browser.ts
  function isSafari (line 20) | function isSafari(): boolean {
  function shouldShowSafariUpdateReminder (line 39) | function shouldShowSafariUpdateReminder(): boolean {
  function isChrome (line 55) | function isChrome(): boolean {
  function isFirefox (line 68) | function isFirefox(): boolean {
  function isMac (line 78) | function isMac(): boolean {
  function getModifierKey (line 96) | function getModifierKey(): string {
  function getBrowserName (line 104) | function getBrowserName(): string {

FILE: src/core/utils/concurrency.ts
  class AsyncLock (line 10) | class AsyncLock {
    method acquire (line 21) | async acquire(key: string, timeout: number = 30000): Promise<() => voi...
    method tryAcquire (line 59) | tryAcquire(key: string): (() => void) | null {
    method release (line 81) | private release(key: string): void {
    method forceRelease (line 89) | private forceRelease(key: string): void {
    method isLocked (line 96) | isLocked(key: string): boolean {
    method getLockDuration (line 103) | getLockDuration(key: string): number | null {
    method withLock (line 115) | async withLock<T>(key: string, fn: () => Promise<T>, timeout?: number)...
    method clearAll (line 127) | clearAll(): void {
  constant LOCK_KEYS (line 142) | const LOCK_KEYS = {
  function withLock (line 153) | function withLock(lockKey: string, timeout?: number) {
  class OperationQueue (line 173) | class OperationQueue {
    method enqueue (line 180) | async enqueue<T>(operation: () => Promise<T>): Promise<T> {
    method process (line 198) | private async process(): Promise<void> {
    method length (line 220) | get length(): number {
    method isProcessing (line 227) | get isProcessing(): boolean {
    method clear (line 234) | clear(): void {

FILE: src/core/utils/extensionContext.ts
  constant EXTENSION_CONTEXT_INVALIDATED_PATTERN (line 1) | const EXTENSION_CONTEXT_INVALIDATED_PATTERN = /extension context invalid...
  function extractMessage (line 3) | function extractMessage(error: unknown): string {
  function isExtensionContextInvalidatedError (line 13) | function isExtensionContextInvalidatedError(error: unknown): boolean {
  function hasValidExtensionContext (line 18) | function hasValidExtensionContext(): boolean {

FILE: src/core/utils/gemini.ts
  constant ENTERPRISE_HINTS (line 1) | const ENTERPRISE_HINTS = ['enterprise', 'workspace', 'workspaces', 'busi...
  constant ENTERPRISE_HOSTS (line 3) | const ENTERPRISE_HOSTS = new Set(['business.gemini.google']);
  type UrlParts (line 5) | type UrlParts = {
  function includesEnterpriseHint (line 12) | function includesEnterpriseHint(value: string): boolean {
  function isGeminiEnterpriseUrl (line 17) | function isGeminiEnterpriseUrl({
  function hasGeminiEnterpriseDomHints (line 29) | function hasGeminiEnterpriseDomHints(doc: Document): boolean {
  function isGeminiEnterpriseEnvironment (line 46) | function isGeminiEnterpriseEnvironment(parts: UrlParts, doc?: Document):...

FILE: src/core/utils/hash.ts
  function hashString (line 10) | function hashString(input: string): string {
  function generateUniqueId (line 24) | function generateUniqueId(prefix = ''): string {
  function hashObject (line 34) | function hashObject(obj: Record<string, unknown>): string {

FILE: src/core/utils/rtl.ts
  constant RTL_LANGUAGES (line 7) | const RTL_LANGUAGES = new Set(['ar', 'he', 'fa', 'ur']);
  function getUrlLanguageHint (line 9) | function getUrlLanguageHint(): string | null {
  function isRTLLanguage (line 25) | function isRTLLanguage(lang: string): boolean {
  function detectRTL (line 37) | function detectRTL(extensionLanguage?: string | null): boolean {
  constant GV_RTL_CLASS (line 58) | const GV_RTL_CLASS = 'gv-rtl';
  function applyRTLClass (line 64) | function applyRTLClass(extensionLanguage?: string | null): boolean {

FILE: src/core/utils/safariStorage.ts
  type SafariStorageAdapter (line 16) | interface SafariStorageAdapter {
  class SafariStorage (line 26) | class SafariStorage implements SafariStorageAdapter {
    method getItem (line 30) | async getItem(key: string): Promise<string | null> {
    method setItem (line 53) | async setItem(key: string, value: string): Promise<void> {
    method removeItem (line 71) | async removeItem(key: string): Promise<void> {
    method migrateFromLocalStorage (line 89) | async migrateFromLocalStorage(key: string): Promise<boolean> {

FILE: src/core/utils/selectors.ts
  function getUserTurnSelectors (line 9) | function getUserTurnSelectors(): string[] {
  function getAssistantTurnSelectors (line 31) | function getAssistantTurnSelectors(): string[] {
  function getConversationSelectors (line 53) | function getConversationSelectors(): string[] {
  function getConversationLinkSelectors (line 60) | function getConversationLinkSelectors(): string[] {
  function combineSelectors (line 67) | function combineSelectors(selectors: string[]): string {

FILE: src/core/utils/storageMigration.ts
  type MigrationResult (line 9) | interface MigrationResult {
  function migrateFromLocalStorage (line 33) | async function migrateFromLocalStorage(
  function isMigrationCompleted (line 138) | async function isMigrationCompleted(
  function getMigrationStatus (line 153) | async function getMigrationStatus(

FILE: src/core/utils/text.ts
  function normalizeText (line 8) | function normalizeText(text: string | null | undefined): string {
  function truncateText (line 21) | function truncateText(text: string, maxLength: number): string {
  function getFirstLines (line 32) | function getFirstLines(text: string, count: number): string {
  function isTruncated (line 44) | function isTruncated(element: HTMLElement): boolean {

FILE: src/core/utils/updateReminder.ts
  constant DEFAULT_MINIMUM_UPDATE_REMINDER_VERSION (line 3) | const DEFAULT_MINIMUM_UPDATE_REMINDER_VERSION = '1.2.3';
  type UpdateReminderPolicyInput (line 5) | interface UpdateReminderPolicyInput {
  function shouldShowUpdateReminderForCurrentVersion (line 12) | function shouldShowUpdateReminderForCurrentVersion({

FILE: src/core/utils/version.ts
  constant EXTENSION_VERSION (line 12) | const EXTENSION_VERSION = manifestChrome.version;
  constant FORMAT_VERSIONS (line 18) | const FORMAT_VERSIONS = {
  type FormatVersion (line 22) | type FormatVersion = keyof typeof FORMAT_VERSIONS;
  type SemanticVersion (line 27) | interface SemanticVersion {
  function parseVersion (line 39) | function parseVersion(version: string): SemanticVersion | null {
  function compareVersions (line 57) | function compareVersions(v1: string, v2: string): number {
  function isVersionCompatible (line 96) | function isVersionCompatible(importVersion: string, formatVersion: Forma...
  function isSupportedFormat (line 119) | function isSupportedFormat(format: string): format is FormatVersion {
  type CompatibilityInfo (line 126) | interface CompatibilityInfo {
  function getCompatibilityInfo (line 138) | function getCompatibilityInfo(
  type VersionMigration (line 183) | interface VersionMigration {
  constant VERSION_MIGRATIONS (line 194) | const VERSION_MIGRATIONS: VersionMigration[] = [
  function applyMigrations (line 210) | function applyMigrations(

FILE: src/features/backup/services/BackupService.ts
  class BackupService (line 24) | class BackupService implements IBackupService {
    method generateBackupFolderName (line 29) | private static generateBackupFolderName(): string {
    method isSupported (line 44) | static isSupported(): boolean {
    method requestDirectoryAccess (line 57) | static async requestDirectoryAccess(): Promise<FileSystemDirectoryHand...
    method generateBackupFiles (line 121) | async generateBackupFiles(config: BackupConfig): Promise<Result<Backup...
    method createBackup (line 208) | async createBackup(
    method shouldBackup (line 272) | shouldBackup(config: BackupConfig): boolean {
    method loadFolderData (line 299) | private async loadFolderData(): Promise<Result<FolderData>> {

FILE: src/features/backup/services/PromptImportExportService.ts
  constant EXPORT_FORMAT (line 12) | const EXPORT_FORMAT = 'gemini-voyager.prompts.v1' as const;
  constant STORAGE_KEY (line 13) | const STORAGE_KEY = 'gvPromptItems';
  class PromptImportExportService (line 18) | class PromptImportExportService {
    method exportToPayload (line 23) | static exportToPayload(items: PromptItem[]): PromptExportPayload {
    method validatePayload (line 35) | static validatePayload(payload: unknown): Result<PromptExportPayload> {
    method loadPrompts (line 114) | static async loadPrompts(): Promise<Result<PromptItem[]>> {
    method savePrompts (line 145) | static async savePrompts(items: PromptItem[]): Promise<Result<void>> {
    method generateExportFilename (line 169) | static generateExportFilename(): string {
    method exportToJSON (line 184) | static async exportToJSON(): Promise<Result<string>> {
    method importFromPayload (line 203) | static async importFromPayload(payload: PromptExportPayload): Promise<
    method downloadJSON (line 281) | static downloadJSON(payload: PromptExportPayload, filename?: string): ...
    method readJSONFile (line 303) | static async readJSONFile(file: File): Promise<Result<unknown>> {

FILE: src/features/backup/types/backup.ts
  type PromptItem (line 10) | interface PromptItem {
  type PromptExportPayload (line 21) | interface PromptExportPayload {
  type BackupConfig (line 31) | interface BackupConfig {
  type BackupMetadata (line 47) | interface BackupMetadata {
  type BackupResult (line 61) | interface BackupResult {
  type BackupFile (line 75) | interface BackupFile {
  type IBackupService (line 83) | interface IBackupService {
  constant BACKUP_STORAGE_KEYS (line 114) | const BACKUP_STORAGE_KEYS = {
  constant DEFAULT_BACKUP_CONFIG (line 121) | const DEFAULT_BACKUP_CONFIG: BackupConfig = {

FILE: src/features/contextSync/adapters/index.ts
  constant ADAPTERS (line 3) | const ADAPTERS: Record<string, AdapterConfig> = {
  function getMatchedAdapter (line 17) | function getMatchedAdapter(host: string): AdapterConfig {

FILE: src/features/contextSync/services/SyncService.ts
  class SyncService (line 3) | class SyncService {
    method constructor (line 7) | private constructor() {}
    method getInstance (line 9) | static getInstance(): SyncService {
    method getServerUrl (line 16) | private async getServerUrl(): Promise<string> {
    method checkServerStatus (line 25) | async checkServerStatus(): Promise<boolean> {
    method syncToIDE (line 41) | async syncToIDE(data: DialogNode[]): Promise<SyncResponse> {

FILE: src/features/contextSync/types.ts
  type DialogNode (line 1) | interface DialogNode {
  type SyncResponse (line 15) | interface SyncResponse {
  type AdapterConfig (line 21) | interface AdapterConfig {

FILE: src/features/export/services/ConversationExportService.ts
  class ConversationExportService (line 29) | class ConversationExportService {
    method export (line 37) | static async export(
    method normalizeError (line 77) | private static normalizeError(error: unknown): string {
    method exportDocument (line 93) | private static async exportDocument(
    method exportJSON (line 123) | private static exportJSON(
    method exportMarkdown (line 177) | private static async exportMarkdown(
    method exportPDF (line 198) | private static async exportPDF(
    method exportImage (line 217) | private static async exportImage(
    method exportDocumentJSON (line 227) | private static exportDocumentJSON(
    method exportDocumentMarkdown (line 252) | private static async exportDocumentMarkdown(
    method exportDocumentPDF (line 271) | private static async exportDocumentPDF(
    method exportDocumentImage (line 291) | private static async exportDocumentImage(
    method extractDocumentContent (line 315) | private static extractDocumentContent(turns: ChatTurn[]): { markdown: ...
    method composeDocumentMarkdown (line 347) | private static composeDocumentMarkdown(content: string, metadata: Conv...
    method hasLeadingMarkdownHeading (line 367) | private static hasLeadingMarkdownHeading(content: string): boolean {
    method formatPlainTextAsHtml (line 371) | private static formatPlainTextAsHtml(content: string): string {
    method downloadMarkdownOrZip (line 380) | private static async downloadMarkdownOrZip(
    method toOriginalSizeUrl (line 456) | private static toOriginalSizeUrl(url: string): string {
    method pickImageExtension (line 466) | private static pickImageExtension(contentType: string | null, url: str...
    method blobToBase64Payload (line 481) | private static blobToBase64Payload(blob: Blob): Promise<string | null> {
    method fetchImageForMarkdownPackaging (line 498) | private static async fetchImageForMarkdownPackaging(
    method downloadJSON (line 597) | private static downloadJSON(data: unknown, filename: string): void {
    method generateFilename (line 620) | private static generateFilename(extension: string, title?: string): st...
    method sanitizeFilenamePart (line 637) | private static sanitizeFilenamePart(title?: string): string {
    method getAvailableFormats (line 653) | static getAvailableFormats(): Array<{

FILE: src/features/export/services/DOMContentExtractor.ts
  type ExtractedContent (line 6) | interface ExtractedContent {
  type ExtractedTurn (line 15) | interface ExtractedTurn {
  function queryOutsideThoughts (line 32) | function queryOutsideThoughts<T extends Element = Element>(
  class DOMContentExtractor (line 44) | class DOMContentExtractor {
    method extractUserContent (line 49) | static extractUserContent(element: HTMLElement): ExtractedContent {
    method extractAssistantContent (line 107) | static extractAssistantContent(element: HTMLElement): ExtractedContent {
    method processNodes (line 284) | private static processNodes(
    method shouldSkipElement (line 529) | private static shouldSkipElement(element: Element): boolean {
    method processInlineContent (line 575) | private static processInlineContent(element: HTMLElement): {
    method extractTextWithInlineFormulas (line 673) | private static extractTextWithInlineFormulas(element: HTMLElement): {
    method extractCodeBlock (line 684) | private static extractCodeBlock(element: HTMLElement): { html: string;...
    method extractCodeFromCodeElement (line 704) | private static extractCodeFromCodeElement(codeEl: HTMLElement): { html...
    method extractTable (line 731) | private static extractTable(element: HTMLElement): { html: string; tex...
    method extractList (line 800) | private static extractList(
    method stripExportArtifacts (line 856) | private static stripExportArtifacts(root: HTMLElement): void {
    method normalizeText (line 885) | private static normalizeText(text: string): string {
    method escapeHtml (line 892) | private static escapeHtml(text: string): string {
    method escapeHtmlAttribute (line 901) | private static escapeHtmlAttribute(text: string): string {

FILE: src/features/export/services/DeepResearchPDFPrintService.ts
  class DeepResearchPDFPrintService (line 9) | class DeepResearchPDFPrintService {
    method export (line 20) | static async export(content: PrintableDocumentContent): Promise<void> {
    method triggerPrint (line 52) | private static triggerPrint(): void {
    method forceStyleFlush (line 60) | private static forceStyleFlush(container: HTMLElement): void {
    method delay (line 68) | private static delay(ms: number): Promise<void> {
    method setTimeoutUnref (line 74) | private static setTimeoutUnref(callback: () => void, ms: number): Retu...
    method registerCleanupHandlers (line 87) | private static registerCleanupHandlers(): void {
    method cleanup (line 107) | private static cleanup(): void {
    method createPrintContainer (line 147) | private static createPrintContainer(content: PrintableDocumentContent)...
    method sanitizePrintableHtml (line 183) | private static sanitizePrintableHtml(html: string): string {
    method extractPlainTextFromHtml (line 204) | private static extractPlainTextFromHtml(html: string): string {
    method normalizeWhitespace (line 213) | private static normalizeWhitespace(text: string): string {
    method formatPlainTextAsHtml (line 221) | private static formatPlainTextAsHtml(text: string): string {
    method normalizeTitle (line 230) | private static normalizeTitle(title: string): string {
    method formatDate (line 239) | private static formatDate(isoString: string): string {
    method injectPrintStyles (line 254) | private static injectPrintStyles(): void {
    method inlineImages (line 516) | private static async inlineImages(container: HTMLElement): Promise<voi...
    method escapeHTML (line 588) | private static escapeHTML(text: string): string {
    method escapeAttribute (line 594) | private static escapeAttribute(text: string): string {

FILE: src/features/export/services/ImageExportService.ts
  type RenderableDocumentContent (line 14) | interface RenderableDocumentContent {
  class ImageExportService (line 22) | class ImageExportService {
    method export (line 27) | static async export(
    method exportDocument (line 40) | static async exportDocument(
    method renderConversationBlob (line 52) | static async renderConversationBlob(
    method renderDocumentBlob (line 61) | static async renderDocumentBlob(content: RenderableDocumentContent): P...
    method createRenderContainer (line 66) | private static createRenderContainer(
    method createDocumentRenderContainer (line 268) | private static createDocumentRenderContainer(content: RenderableDocume...
    method inlineImages (line 380) | private static async inlineImages(container: HTMLElement): Promise<voi...
    method renderWithSafariFallback (line 467) | private static async renderWithSafariFallback(container: HTMLElement):...
    method renderContainerToBlob (line 481) | private static async renderContainerToBlob(container: HTMLElement): Pr...
    method shouldRetryPrimaryRender (line 496) | private static shouldRetryPrimaryRender(error: unknown): boolean {
    method downloadBlob (line 506) | private static downloadBlob(blob: Blob, filename: string): void {
    method escapeHTML (line 523) | private static escapeHTML(text: string): string {
    method escapeAttr (line 529) | private static escapeAttr(text: string): string {
    method formatPlainTextAsHtml (line 538) | private static formatPlainTextAsHtml(text: string): string {
    method formatDate (line 548) | private static formatDate(iso: string): string {

FILE: src/features/export/services/ImageRenderService.ts
  constant TRANSPARENT_IMAGE_PLACEHOLDER (line 3) | const TRANSPARENT_IMAGE_PLACEHOLDER =
  constant DEFAULT_OFFSCREEN_LEFT (line 5) | const DEFAULT_OFFSCREEN_LEFT = '-100000px';
  constant DEFAULT_SANITIZE_SELECTOR (line 6) | const DEFAULT_SANITIZE_SELECTOR = 'img, video, iframe, canvas, svg image';
  constant DEFAULT_RENDER_WIDTH (line 7) | const DEFAULT_RENDER_WIDTH = 720;
  type RenderElementToImageBlobOptions (line 9) | type RenderElementToImageBlobOptions = {
  function isImageResourceRenderError (line 18) | function isImageResourceRenderError(error: unknown): boolean {
  function renderTargetToBlob (line 33) | async function renderTargetToBlob(target: HTMLElement): Promise<Blob> {
  function sanitizeClone (line 53) | function sanitizeClone(target: HTMLElement, selector: string): HTMLEleme...
  function resolveRenderableWidth (line 59) | function resolveRenderableWidth(target: HTMLElement): number {
  function renderUsingSanitizedClone (line 80) | async function renderUsingSanitizedClone(target: HTMLElement, selector: ...
  function delay (line 105) | async function delay(ms: number): Promise<void> {
  function renderElementToImageBlob (line 109) | async function renderElementToImageBlob(

FILE: src/features/export/services/MarkdownFormatter.ts
  class MarkdownFormatter (line 13) | class MarkdownFormatter {
    method fetchAsDataURL (line 17) | private static async fetchAsDataURL(url: string): Promise<string | nul...
    method extractImageUrls (line 40) | static extractImageUrls(markdown: string): string[] {
    method rewriteImageUrls (line 53) | static rewriteImageUrls(markdown: string, mapping: Map<string, string>...
    method degradeImageMarkdownForSafari (line 64) | static degradeImageMarkdownForSafari(markdown: string): string {
    method format (line 76) | static format(turns: ChatTurn[], metadata: ConversationMetadata): stri...
    method formatHeader (line 104) | private static formatHeader(metadata: ConversationMetadata): string {
    method formatTurn (line 123) | private static formatTurn(turn: ChatTurn, index: number): string {
    method formatContent (line 209) | private static formatContent(content: string): string {
    method formatFooter (line 227) | private static formatFooter(metadata: ConversationMetadata): string {
    method extractTitleFromURL (line 237) | private static extractTitleFromURL(url: string): string {
    method formatDate (line 259) | private static formatDate(isoString: string): string {
    method escapeMarkdown (line 277) | private static escapeMarkdown(text: string): string {
    method generateFilename (line 285) | static generateFilename(): string {
    method download (line 300) | static download(content: string, filename?: string): void {

FILE: src/features/export/services/PDFPrintService.ts
  type PrintableDocumentContent (line 11) | interface PrintableDocumentContent {
  class PDFPrintService (line 23) | class PDFPrintService {
    method export (line 36) | static async export(
    method exportDocument (line 44) | static async exportDocument(content: PrintableDocumentContent): Promis...
    method exportInternal (line 69) | private static async exportInternal(
    method triggerPrint (line 120) | private static triggerPrint(): void {
    method forceStyleFlush (line 128) | private static forceStyleFlush(container: HTMLElement): void {
    method delay (line 138) | private static delay(ms: number): Promise<void> {
    method registerCleanupHandlers (line 144) | private static registerCleanupHandlers(): void {
    method setTimeoutUnref (line 164) | private static setTimeoutUnref(callback: () => void, ms: number): Retu...
    method createPrintContainer (line 181) | private static createPrintContainer(
    method extractPlainTextFromHtml (line 202) | private static extractPlainTextFromHtml(html: string): string {
    method normalizeWhitespace (line 211) | private static normalizeWhitespace(text: string): string {
    method inlineImages (line 222) | private static async inlineImages(container: HTMLElement): Promise<voi...
    method getConversationTitle (line 306) | private static getConversationTitle(): string {
    method isMeaningfulConversationTitle (line 371) | private static isMeaningfulConversationTitle(title: string | null | un...
    method isGemLabel (line 387) | private static isGemLabel(text: string | null | undefined): boolean {
    method extractConversationIdFromURL (line 392) | private static extractConversationIdFromURL(url: string): string | null {
    method extractTitleFromLinkText (line 405) | private static extractTitleFromLinkText(link?: HTMLAnchorElement | nul...
    method extractTitleFromConversationElement (line 419) | private static extractTitleFromConversationElement(conversationEl: HTM...
    method extractTitleFromNativeSidebarByConversationId (line 466) | private static extractTitleFromNativeSidebarByConversationId(
    method renderHeader (line 492) | private static renderHeader(
    method renderContent (line 524) | private static renderContent(turns: ChatTurn[]): string {
    method renderTurn (line 535) | private static renderTurn(turn: ChatTurn, index: number): string {
    method formatContent (line 606) | private static formatContent(content: string): string {
    method renderFooter (line 624) | private static renderFooter(metadata: ConversationMetadata): string {
    method injectPrintStyles (line 636) | private static injectPrintStyles(fontSize?: number): void {
    method cleanup (line 976) | private static cleanup(): void {
    method extractTitleFromURL (line 1022) | private static extractTitleFromURL(url: string): string {
    method formatDate (line 1040) | private static formatDate(isoString: string): string {
    method normalizeConversationTitle (line 1055) | private static normalizeConversationTitle(rawTitle: string | undefined...
    method getPrintDialogTitle (line 1066) | private static getPrintDialogTitle(
    method escapeHTML (line 1085) | private static escapeHTML(text: string): string {
    method escapeCssAttributeValue (line 1091) | private static escapeCssAttributeValue(value: string): string {
    method escapeAttribute (line 1099) | private static escapeAttribute(text: string): string {

FILE: src/features/export/services/__tests__/ConversationExportService.test.ts
  function setUserAgentVendor (line 29) | function setUserAgentVendor(userAgent: string, vendor: string): void {
  type JSZipFileFn (line 488) | type JSZipFileFn = (name: unknown, data?: unknown, options?: unknown) =>...

FILE: src/features/export/services/__tests__/DeepResearchPDFPrintService.test.ts
  function setUserAgentVendor (line 5) | function setUserAgentVendor(userAgent: string, vendor: string): void {

FILE: src/features/export/services/__tests__/ImageExportService.test.ts
  function setUserAgentVendor (line 13) | function setUserAgentVendor(userAgent: string, vendor: string): void {

FILE: src/features/export/services/__tests__/PDFPrintService.safari.test.ts
  function mockSafariUserAgent (line 12) | function mockSafariUserAgent(): void {

FILE: src/features/export/types/errors.ts
  constant IMAGE_RENDER_EVENT_ERROR_CODE (line 1) | const IMAGE_RENDER_EVENT_ERROR_CODE = 'image_render_event_error' as const;
  function isEventLikeImageRenderError (line 3) | function isEventLikeImageRenderError(error: unknown): boolean {

FILE: src/features/export/types/export.ts
  type ChatTurn (line 9) | interface ChatTurn {
  type ConversationMetadata (line 22) | interface ConversationMetadata {
  type ExportFormat (line 32) | enum ExportFormat {
  type ExportLayout (line 39) | type ExportLayout = 'conversation' | 'document';
  type ExportFormatInfo (line 44) | interface ExportFormatInfo {
  type ExportOptions (line 55) | interface ExportOptions {
  type BaseExportPayload (line 74) | interface BaseExportPayload {
  type JSONExportPayload (line 89) | interface JSONExportPayload extends BaseExportPayload {
  type ExportResult (line 97) | interface ExportResult {

FILE: src/features/export/ui/ExportDialog.ts
  type ExportDialogOptions (line 10) | interface ExportDialogOptions {
  constant PDF_DEFAULT_FONT_SIZE (line 31) | const PDF_DEFAULT_FONT_SIZE = 11;
  constant IMAGE_DEFAULT_FONT_SIZE (line 32) | const IMAGE_DEFAULT_FONT_SIZE = 20;
  constant PDF_MIN (line 33) | const PDF_MIN = 8;
  constant PDF_MAX (line 34) | const PDF_MAX = 16;
  constant IMAGE_MIN (line 35) | const IMAGE_MIN = 14;
  constant IMAGE_MAX (line 36) | const IMAGE_MAX = 28;
  class ExportDialog (line 38) | class ExportDialog {
    method show (line 46) | show(options: ExportDialogOptions): void {
    method hide (line 58) | hide(): void {
    method createDialog (line 68) | private createDialog(options: ExportDialogOptions): HTMLElement {
    method createFormatOption (line 170) | private createFormatOption(
    method createFontSizeSection (line 243) | private createFontSizeSection(options: ExportDialogOptions): HTMLEleme...
    method updateFontSizeSection (line 296) | private updateFontSizeSection(): void {

FILE: src/features/export/ui/ExportErrorMessage.ts
  function resolveExportErrorMessage (line 3) | function resolveExportErrorMessage(

FILE: src/features/export/ui/ExportToast.ts
  type ExportToastOptions (line 1) | type ExportToastOptions = {
  constant TOAST_SELECTOR (line 5) | const TOAST_SELECTOR = '.gv-export-toast';
  constant TOAST_TRANSITION_MS (line 6) | const TOAST_TRANSITION_MS = 300;
  constant DEFAULT_AUTO_DISMISS_MS (line 7) | const DEFAULT_AUTO_DISMISS_MS = 2200;
  function getOrCreateToast (line 11) | function getOrCreateToast(): HTMLDivElement {
  function showExportToast (line 23) | function showExportToast(message: string, options?: ExportToastOptions):...

FILE: src/features/export/ui/__tests__/ExportDialog.safariHint.test.ts
  function setUserAgentVendor (line 11) | function setUserAgentVendor(userAgent: string, vendor: string): void {

FILE: src/features/folder/services/FolderImportExportService.ts
  constant EXPORT_FORMAT (line 26) | const EXPORT_FORMAT: FormatVersion = 'gemini-voyager.folders.v1' as const;
  class FolderImportExportService (line 31) | class FolderImportExportService {
    method exportToPayload (line 36) | static exportToPayload(data: FolderData): FolderExportPayload {
    method validatePayload (line 52) | static validatePayload(payload: unknown): Result<FolderExportPayload, ...
    method mergeData (line 179) | static mergeData(
    method importFromPayloadInternal (line 242) | private static importFromPayloadInternal(
    method importFromPayload (line 338) | static async importFromPayload(
    method generateExportFilename (line 354) | static generateExportFilename(): string {
    method downloadJSON (line 369) | static downloadJSON(payload: FolderExportPayload, filename?: string): ...
    method readJSONFile (line 391) | static async readJSONFile(file: File): Promise<Result<unknown>> {
    method restoreFromBackup (line 412) | static restoreFromBackup(): Result<FolderData> {
    method clearBackup (line 440) | static clearBackup(): void {
    method hasBackup (line 452) | static hasBackup(): boolean {
    method getBackupTimestamp (line 463) | static getBackupTimestamp(): string | null {

FILE: src/features/folder/types/import-export.ts
  type FolderExportPayload (line 9) | interface FolderExportPayload {
  type ImportStrategy (line 19) | type ImportStrategy = 'merge' | 'overwrite';
  type ImportOptions (line 24) | interface ImportOptions {
  type ImportResult (line 32) | interface ImportResult {
  type ValidationErrorType (line 43) | enum ValidationErrorType {
  type ValidationError (line 53) | interface ValidationError {

FILE: src/features/formulaCopy/FormulaCopyService.test.ts
  class TestBlob (line 39) | class TestBlob {
    method constructor (line 42) | constructor(parts: BlobPart[], _options?: BlobPropertyBag) {
    method text (line 46) | public async text(): Promise<string> {
  class TestClipboardItem (line 51) | class TestClipboardItem {
    method constructor (line 54) | constructor(dataByType: Record<string, Blob>) {
  function resetSingleton (line 59) | function resetSingleton(): void {

FILE: src/features/formulaCopy/FormulaCopyService.ts
  type FormulaCopyFormat (line 16) | type FormulaCopyFormat = 'latex' | 'unicodemath' | 'no-dollar';
  type FormulaCopyConfig (line 21) | interface FormulaCopyConfig {
  class FormulaCopyService (line 32) | class FormulaCopyService {
    method constructor (line 56) | private constructor(config: FormulaCopyConfig = {}) {
    method getInstance (line 71) | public static getInstance(config?: FormulaCopyConfig): FormulaCopyServ...
    method loadI18nMessages (line 81) | private loadI18nMessages(): void {
    method loadFormatPreference (line 99) | private async loadFormatPreference(): Promise<void> {
    method initialize (line 118) | public initialize(): void {
    method destroy (line 132) | public destroy(): void {
    method extractLatexSource (line 181) | private extractLatexSource(element: HTMLElement): string | null {
    method copyFormula (line 206) | private async copyFormula(
    method copyToClipboard (line 231) | private async copyToClipboard(text: string, html?: string): Promise<bo...
    method copyToClipboardLegacy (line 276) | private copyToClipboardLegacy(text: string): boolean {
    method isMathMLClipboardUnsupported (line 297) | private isMathMLClipboardUnsupported(error: unknown): boolean {
    method getErrorMessage (line 313) | private getErrorMessage(error: unknown): string | null {
    method getErrorName (line 321) | private getErrorName(error: unknown): string | null {
    method findMathElement (line 337) | private findMathElement(target: HTMLElement): HTMLElement | null {
    method isMathContainer (line 372) | private isMathContainer(element: HTMLElement): boolean {
    method isDisplayMode (line 380) | private isDisplayMode(element: HTMLElement): boolean {
    method wrapFormula (line 410) | private wrapFormula(formula: string, isDisplayMode: boolean): { text: ...
    method ensureMathMLNamespace (line 444) | private ensureMathMLNamespace(mathML: string): string {
    method toWordMathML (line 452) | private toWordMathML(mathML: string): string {
    method cloneNodeWithMathMLPrefix (line 509) | private cloneNodeWithMathMLPrefix(targetDocument: Document, sourceNode...
    method wrapMathMLForWordHtml (line 542) | private wrapMathMLForWordHtml(mathML: string): string {
    method stripMathMLAnnotations (line 553) | private stripMathMLAnnotations(mathML: string): string {
    method stripPresentationAttributes (line 559) | private stripPresentationAttributes(root: Element): void {
    method stripMathDelimiters (line 577) | private stripMathDelimiters(formula: string): string {
    method findDataMathInSubtree (line 602) | private findDataMathInSubtree(root: HTMLElement): HTMLElement | null {
    method showToast (line 610) | private showToast(message: string, x: number, y: number, isSuccess: bo...
    method createCopyToast (line 638) | private createCopyToast(): HTMLDivElement {
    method removeCopyToast (line 648) | private removeCopyToast(): void {
    method isServiceInitialized (line 658) | public isServiceInitialized(): boolean {

FILE: src/features/formulaCopy/index.ts
  function startFormulaCopy (line 12) | function startFormulaCopy(): void {
  function stopFormulaCopy (line 17) | function stopFormulaCopy(): void {

FILE: src/hooks/useDarkMode.ts
  function useDarkMode (line 3) | function useDarkMode() {

FILE: src/hooks/useWidthAdjuster.ts
  type UseWidthAdjusterOptions (line 3) | interface UseWidthAdjusterOptions {
  function useWidthAdjuster (line 17) | function useWidthAdjuster({

FILE: src/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: src/pages/background/index.ts
  constant CUSTOM_CONTENT_SCRIPT_ID (line 18) | const CUSTOM_CONTENT_SCRIPT_ID = 'gv-custom-content-script';
  constant CUSTOM_WEBSITE_KEY (line 19) | const CUSTOM_WEBSITE_KEY = 'gvPromptCustomWebsites';
  constant FETCH_INTERCEPTOR_SCRIPT_ID (line 20) | const FETCH_INTERCEPTOR_SCRIPT_ID = 'gv-fetch-interceptor';
  constant GEMINI_MATCHES (line 23) | const GEMINI_MATCHES = [
  function isSyncAccountScope (line 29) | function isSyncAccountScope(value: unknown): value is SyncAccountScope {
  function toSyncAccountScope (line 40) | function toSyncAccountScope(scope: AccountScope): SyncAccountScope {
  function resolveAccountScopeForMessage (line 48) | async function resolveAccountScopeForMessage(
  function matchesRouteScope (line 66) | function matchesRouteScope(url: string, routeUserId: string | null): boo...
  function filterStarredByRouteScope (line 72) | function filterStarredByRouteScope(
  function filterForkNodesByRouteScope (line 91) | function filterForkNodesByRouteScope(
  function registerFetchInterceptor (line 128) | async function registerFetchInterceptor(): Promise<void> {
  constant MANIFEST_DEFAULT_DOMAINS (line 165) | const MANIFEST_DEFAULT_DOMAINS = new Set(
  function patternToDomain (line 174) | function patternToDomain(pattern: string | undefined): string | null {
  function toMatchPatterns (line 185) | function toMatchPatterns(domain: string): string[] {
  function extractDomainsFromOrigins (line 198) | function extractDomainsFromOrigins(origins?: string[]): string[] {
  function filterGrantedOrigins (line 207) | async function filterGrantedOrigins(patterns: string[]): Promise<string[...
  function syncCustomContentScripts (line 224) | async function syncCustomContentScripts(domains?: string[]): Promise<voi...
  class StarredMessagesManager (line 328) | class StarredMessagesManager {
    method serialize (line 334) | private serialize<T>(operation: () => Promise<T>): Promise<T> {
    method getFromStorage (line 340) | private async getFromStorage(): Promise<StarredMessagesData> {
    method saveToStorage (line 350) | private async saveToStorage(data: StarredMessagesData): Promise<void> {
    method addStarredMessage (line 354) | async addStarredMessage(message: StarredMessage): Promise<boolean> {
    method removeStarredMessage (line 384) | async removeStarredMessage(conversationId: string, turnId: string): Pr...
    method getAllStarredMessages (line 408) | async getAllStarredMessages(): Promise<StarredMessagesData> {
    method getStarredMessagesForConversation (line 412) | async getStarredMessagesForConversation(conversationId: string): Promi...
    method isMessageStarred (line 417) | async isMessageStarred(conversationId: string, turnId: string): Promis...
  class ForkNodesManager (line 429) | class ForkNodesManager {
    method serialize (line 432) | private serialize<T>(operation: () => Promise<T>): Promise<T> {
    method getFromStorage (line 438) | private async getFromStorage(): Promise<ForkNodesData> {
    method saveToStorage (line 448) | private async saveToStorage(data: ForkNodesData): Promise<void> {
    method addForkNode (line 452) | async addForkNode(node: ForkNode): Promise<boolean> {
    method removeForkNode (line 483) | async removeForkNode(
    method getAllForkNodes (line 519) | async getAllForkNodes(): Promise<ForkNodesData> {
    method getForConversation (line 523) | async getForConversation(conversationId: string): Promise<ForkNode[]> {
    method getGroup (line 528) | async getGroup(forkGroupId: string): Promise<ForkNode[]> {
  function arrayBufferToBase64 (line 946) | function arrayBufferToBase64(buffer: ArrayBuffer): string {

FILE: src/pages/content/changelog/index.ts
  constant MARKDOWN_IMAGE_URL_REGEX (line 21) | const MARKDOWN_IMAGE_URL_REGEX = /!\[([^\]]*)\]\((https?:\/\/[^\s)]+)\)/g;
  constant GITHUB_PROMOTION_PATH_PREFIX (line 23) | const GITHUB_PROMOTION_PATH_PREFIX =
  constant RAW_GITHUBUSERCONTENT_PROMOTION_PATH_PREFIX (line 25) | const RAW_GITHUBUSERCONTENT_PROMOTION_PATH_PREFIX =
  function getPromotionRuntimePath (line 28) | function getPromotionRuntimePath(filename: string): string | null {
  function getRuntimeUrl (line 43) | function getRuntimeUrl(path: string): string | null {
  function extractPromotionRuntimePath (line 63) | function extractPromotionRuntimePath(url: URL): string | null {
  function resolveChangelogImageUrl (line 76) | function resolveChangelogImageUrl(
  function rewriteChangelogImageUrls (line 92) | function rewriteChangelogImageUrls(
  function stripFrontMatter (line 109) | function stripFrontMatter(raw: string): string {
  function extractLocalizedContent (line 118) | function extractLocalizedContent(raw: string, lang: AppLanguage): string {
  function t (line 142) | function t(key: TranslationKey, lang: AppLanguage): string {
  function getDocsUrl (line 150) | function getDocsUrl(lang: AppLanguage): string {
  function getSponsorUrl (line 161) | function getSponsorUrl(lang: AppLanguage): string {
  function showImageLightbox (line 171) | function showImageLightbox(src: string, alt: string): void {
  constant CHROME_STORE_URL (line 196) | const CHROME_STORE_URL =
  function readNotifyMode (line 202) | async function readNotifyMode(): Promise<'popup' | 'badge'> {
  function createChangelogModal (line 215) | function createChangelogModal(
  function showChangelogModal (line 478) | async function showChangelogModal(
  function openChangelog (line 554) | async function openChangelog(): Promise<void> {
  function hasUnreadChangelog (line 561) | async function hasUnreadChangelog(): Promise<boolean> {
  function showChangelogModalDirect (line 575) | async function showChangelogModalDirect(): Promise<void> {
  function startChangelog (line 606) | async function startChangelog(): Promise<() => void> {

FILE: src/pages/content/chatWidth/__tests__/chatWidth.test.ts
  constant STYLE_ID (line 3) | const STYLE_ID = 'gemini-voyager-chat-width';
  constant STORAGE_KEY (line 4) | const STORAGE_KEY = 'geminiChatWidth';
  constant MOCK_SCREEN_WIDTH (line 5) | const MOCK_SCREEN_WIDTH = 1920;
  type StorageChangeListener (line 7) | type StorageChangeListener = (
  function getInjectedStyle (line 12) | function getInjectedStyle(): HTMLStyleElement {
  function percentToPixels (line 18) | function percentToPixels(percent: number): number {
  function expectTableRuleWidth (line 22) | function expectTableRuleWidth(styleText: string, percent: number): void {
  function expectSingleTableScrollbarRules (line 31) | function expectSingleTableScrollbarRules(styleText: string): void {

FILE: src/pages/content/chatWidth/index.ts
  constant STYLE_ID (line 5) | const STYLE_ID = 'gemini-voyager-chat-width';
  constant DEFAULT_PERCENT (line 6) | const DEFAULT_PERCENT = 70;
  constant MIN_PERCENT (line 7) | const MIN_PERCENT = 30;
  constant MAX_PERCENT (line 8) | const MAX_PERCENT = 100;
  constant LEGACY_BASELINE_PX (line 9) | const LEGACY_BASELINE_PX = 1200;
  function getUserSelectors (line 12) | function getUserSelectors(): string[] {
  function getAssistantSelectors (line 24) | function getAssistantSelectors(): string[] {
  function getTableSelectors (line 38) | function getTableSelectors(): string[] {
  function applyWidth (line 62) | function applyWidth(widthPercent: number) {
  function removeStyles (line 225) | function removeStyles() {
  constant ENABLED_KEY (line 232) | const ENABLED_KEY = 'gvChatWidthEnabled';
  function startChatWidthAdjuster (line 234) | function startChatWidthAdjuster() {

FILE: src/pages/content/contextSync/capture.ts
  class ContextCaptureService (line 6) | class ContextCaptureService {
    method constructor (line 9) | private constructor() {}
    method getInstance (line 11) | static getInstance(): ContextCaptureService {
    method captureDialogue (line 18) | async captureDialogue(): Promise<DialogNode[]> {
    method getBase64Safe (line 53) | private static async getBase64Safe(url: string): Promise<string | null> {
    method convertTableToMarkdown (line 169) | private convertTableToMarkdown(table: HTMLTableElement): string {
    method extractNodeInfo (line 205) | private async extractNodeInfo(

FILE: src/pages/content/contextSync/index.ts
  function startContextSync (line 5) | function startContextSync() {
  function handleSyncRequest (line 17) | async function handleSyncRequest(sendResponse: (response: unknown) => vo...

FILE: src/pages/content/deepResearch/__tests__/menuButton.test.ts
  function createNativeMenuButton (line 16) | function createNativeMenuButton({
  function createDeepResearchReportMenuPanel (line 55) | function createDeepResearchReportMenuPanel(): HTMLElement {

FILE: src/pages/content/deepResearch/download.ts
  function generateFilename (line 8) | function generateFilename(): string {
  function downloadMarkdown (line 24) | function downloadMarkdown(content: string): void {

FILE: src/pages/content/deepResearch/extractor.ts
  function extractThoughtItem (line 10) | function extractThoughtItem(thoughtElement: Element): { header: string; ...
  function extractBrowseChips (line 37) | function extractBrowseChips(browseListElement: Element): BrowseChip[] {
  function extractThinkingSection (line 71) | function extractThinkingSection(panelElement: Element): ThinkingSection ...
  function extractThinkingPanels (line 120) | function extractThinkingPanels(): ThinkingContent | null {
  function getConversationTitle (line 163) | function getConversationTitle(): string {

FILE: src/pages/content/deepResearch/formatter.ts
  function formatThoughtItem (line 11) | function formatThoughtItem(header: string, content: string): string {
  function formatBrowseChips (line 32) | async function formatBrowseChips(chips: BrowseChip[]): Promise<string> {
  function formatThinkingItem (line 59) | async function formatThinkingItem(item: ThinkingItem): Promise<string> {
  function formatThinkingSection (line 71) | async function formatThinkingSection(section: ThinkingSection, index: nu...
  function formatTimestamp (line 101) | function formatTimestamp(isoString: string): string {
  function formatToMarkdown (line 121) | async function formatToMarkdown(content: ThinkingContent): Promise<strin...

FILE: src/pages/content/deepResearch/index.ts
  function isDeepResearchConversation (line 10) | function isDeepResearchConversation(): boolean {
  function getMenuPanelsFromNode (line 14) | function getMenuPanelsFromNode(node: HTMLElement): HTMLElement[] {
  function observeMenuOpening (line 28) | function observeMenuOpening(): void {
  function startDeepResearchExport (line 59) | function startDeepResearchExport(): void {

FILE: src/pages/content/deepResearch/menuButton.ts
  type Dictionaries (line 28) | type Dictionaries = Record<AppLanguage, Record<string, string>>;
  constant DOWNLOAD_BUTTON_CLASS (line 29) | const DOWNLOAD_BUTTON_CLASS = 'gv-deep-research-download';
  constant SAVE_REPORT_BUTTON_CLASS (line 30) | const SAVE_REPORT_BUTTON_CLASS = 'gv-deep-research-save-report';
  constant INJECTED_BUTTON_CLASSES (line 31) | const INJECTED_BUTTON_CLASSES = [DOWNLOAD_BUTTON_CLASS, SAVE_REPORT_BUTT...
  constant TEMPLATE_EXCLUDED_CLASS_NAMES (line 32) | const TEMPLATE_EXCLUDED_CLASS_NAMES = [...INJECTED_BUTTON_CLASSES, 'shar...
  function waitForElement (line 37) | function waitForElement(selector: string, timeout: number = 5000): Promi...
  function loadDictionaries (line 67) | async function loadDictionaries(): Promise<Dictionaries> {
  function applyDeepResearchDownloadButtonI18n (line 112) | function applyDeepResearchDownloadButtonI18n(
  function applyDeepResearchSaveReportButtonI18n (line 124) | function applyDeepResearchSaveReportButtonI18n(
  function getLanguage (line 139) | async function getLanguage(): Promise<AppLanguage> {
  function handleDownload (line 179) | async function handleDownload(): Promise<void> {
  function createMenuButtonFallback (line 199) | function createMenuButtonFallback({
  function createMenuButton (line 254) | function createMenuButton({
  function createDownloadButton (line 295) | function createDownloadButton(
  function sanitizeFilenamePart (line 310) | function sanitizeFilenamePart(value: string): string {
  function buildReportFilename (line 320) | function buildReportFilename(format: ExportFormat, title: string): string {
  function showDeepResearchExportProgressOverlay (line 328) | function showDeepResearchExportProgressOverlay(
  function handleSaveReport (line 361) | function handleSaveReport(dict: Dictionaries, lang: AppLanguage): void {
  function createSaveReportButton (line 433) | function createSaveReportButton(
  type StorageChange (line 453) | type StorageChange = { newValue?: unknown };
  type StorageChanges (line 454) | type StorageChanges = Record<string, StorageChange>;
  type StorageOnChanged (line 456) | type StorageOnChanged = {
  type ExtensionStorage (line 461) | type ExtensionStorage = {
  function getExtensionStorage (line 465) | function getExtensionStorage(): ExtensionStorage | null {
  function isDeepResearchReportMenuPanel (line 473) | function isDeepResearchReportMenuPanel(menuPanel: HTMLElement): boolean {
  function injectDownloadButton (line 492) | async function injectDownloadButton(targetMenuPanel?: HTMLElement): Prom...

FILE: src/pages/content/deepResearch/reportExtractor.ts
  constant GENERIC_TITLES (line 7) | const GENERIC_TITLES = new Set([
  function isMeaningfulTitle (line 15) | function isMeaningfulTitle(title: string): boolean {
  function scoreCandidate (line 23) | function scoreCandidate(el: HTMLElement): number {
  function findDeepResearchReportRoot (line 34) | function findDeepResearchReportRoot(): HTMLElement | null {
  function extractDeepResearchReportTitle (line 57) | function extractDeepResearchReportTitle(reportRoot: HTMLElement): string {

FILE: src/pages/content/deepResearch/types.ts
  type ThoughtItem (line 5) | interface ThoughtItem {
  type BrowseChip (line 11) | interface BrowseChip {
  type BrowseChipGroup (line 17) | interface BrowseChipGroup {
  type ThinkingItem (line 22) | type ThinkingItem = ThoughtItem | BrowseChipGroup;
  type ThinkingSection (line 24) | interface ThinkingSection {
  type ThinkingContent (line 28) | interface ThinkingContent {

FILE: src/pages/content/defaultModel/modelLocker.ts
  type DefaultModelSetting (line 5) | type DefaultModelSetting =
  type StoredDefaultModelSetting (line 9) | type StoredDefaultModelSetting = { id: string; name: string };
  constant FAST_MODEL_IDS (line 12) | const FAST_MODEL_IDS = new Set([
  constant FAST_MODEL_NAMES (line 17) | const FAST_MODEL_NAMES = ['flash', '2.0 flash', 'gemini 2.0 flash', 'fas...
  constant MODE_ITEM_SELECTOR (line 20) | const MODE_ITEM_SELECTOR = '[role="menuitemradio"], [role="menuitem"]';
  constant NON_MODEL_MENU_EXCLUSION_FALLBACK (line 23) | const NON_MODEL_MENU_EXCLUSION_FALLBACK =
  constant CHAT_INPUT_SELECTORS (line 26) | const CHAT_INPUT_SELECTORS = [
  class DefaultModelManager (line 37) | class DefaultModelManager {
    method constructor (line 59) | private constructor() {}
    method getInstance (line 61) | public static getInstance(): DefaultModelManager {
    method init (line 68) | public async init() {
    method destroy (line 124) | public destroy(): void {
    method initObserver (line 167) | private initObserver() {
    method resolveModeSwitchContainer (line 187) | private resolveModeSwitchContainer(root: HTMLElement): HTMLElement | n...
    method getModeSwitchMenuPanel (line 203) | private getModeSwitchMenuPanel(): HTMLElement | null {
    method waitForModeSwitchMenuPanel (line 213) | private async waitForModeSwitchMenuPanel(timeoutMs: number): Promise<H...
    method scheduleMenuPanelInjection (line 224) | private scheduleMenuPanelInjection(menuPanel: HTMLElement) {
    method injectStarButtons (line 253) | private async injectStarButtons(menuPanel: HTMLElement): Promise<boole...
    method getModelNameFromItem (line 342) | private getModelNameFromItem(item: HTMLElement): string {
    method getModelIdFromItem (line 347) | private getModelIdFromItem(item: HTMLElement): string | null {
    method isDefaultForItem (line 365) | private isDefaultForItem(
    method updateStarState (line 378) | private updateStarState(
    method getStarIcon (line 405) | private getStarIcon(filled: boolean): string {
    method handleStarClick (line 413) | private async handleStarClick(modelName: string, btn: HTMLElement) {
    method showToast (line 469) | private showToast(message: string) {
    method checkAndLockModelWithDelay (line 499) | private async checkAndLockModelWithDelay() {
    method checkAndLockModel (line 505) | private async checkAndLockModel() {
    method isNewConversation (line 553) | private isNewConversation() {
    method isFastModel (line 564) | private isFastModel(model: DefaultModelSetting): boolean {
    method tryLockToModel (line 574) | private async tryLockToModel(targetModel: DefaultModelSetting) {
    method focusChatInputAfterAutoSwitch (line 708) | private focusChatInputAfterAutoSwitch(): void {
    method findChatInputElement (line 722) | private findChatInputElement(): HTMLElement | null {
    method parseStoredDefaultModel (line 734) | private parseStoredDefaultModel(value: unknown): DefaultModelSetting |...
    method isStoredDefaultModelSetting (line 750) | private isStoredDefaultModelSetting(value: unknown): value is StoredDe...

FILE: src/pages/content/editInputWidth/index.ts
  constant STYLE_ID (line 8) | const STYLE_ID = 'gemini-voyager-edit-input-width';
  constant DEFAULT_PERCENT (line 9) | const DEFAULT_PERCENT = 60;
  constant MIN_PERCENT (line 10) | const MIN_PERCENT = 30;
  constant MAX_PERCENT (line 11) | const MAX_PERCENT = 100;
  constant LEGACY_BASELINE_PX (line 12) | const LEGACY_BASELINE_PX = 1200;
  function getEditModeSelectors (line 30) | function getEditModeSelectors(): string[] {
  function applyWidth (line 38) | function applyWidth(widthPercent: number): void {
  function removeStyles (line 152) | function removeStyles(): void {
  constant ENABLED_KEY (line 159) | const ENABLED_KEY = 'gvEditInputWidthEnabled';
  function startEditInputWidthAdjuster (line 164) | function startEditInputWidthAdjuster(): void {

FILE: src/pages/content/export/__tests__/conversationMenuInjection.test.ts
  function createNativeMenuButton (line 12) | function createNativeMenuButton(
  function createConversationMenuPanel (line 50) | function createConversationMenuPanel(useFontIcon: boolean = true): HTMLE...
  function createResponseMenuPanel (line 68) | function createResponseMenuPanel(useFontIcon: boolean = true): HTMLEleme...

FILE: src/pages/content/export/__tests__/responseActionImageButton.test.ts
  function createNativeActionButton (line 5) | function createNativeActionButton({
  function createAssistantActionBar (line 32) | function createAssistantActionBar(): HTMLElement {
  function createNestedAssistantActionBar (line 63) | function createNestedAssistantActionBar(): HTMLElement {

FILE: src/pages/content/export/__tests__/responseImageCopy.test.ts
  class MockClipboardItem (line 14) | class MockClipboardItem {
    method constructor (line 17) | constructor(data: Record<string, Blob>) {

FILE: src/pages/content/export/__tests__/sidebarConversationTarget.test.ts
  function asRect (line 5) | function asRect(left: number, top: number, width: number, height: number...
  function setRect (line 21) | function setRect(el: Element, left: number, top: number, width: number =...
  function createConversation (line 25) | function createConversation(id: string, title: string): HTMLAnchorElement {

FILE: src/pages/content/export/__tests__/topNodePreload.test.ts
  function flushMicrotasks (line 8) | async function flushMicrotasks(): Promise<void> {

FILE: src/pages/content/export/conversationDom.ts
  type ResolveConversationRootOptions (line 1) | type ResolveConversationRootOptions = {
  constant CONVERSATION_ROOT_CANDIDATES (line 6) | const CONVERSATION_ROOT_CANDIDATES = [
  function filterOutDeepResearchImmersiveNodes (line 13) | function filterOutDeepResearchImmersiveNodes<T extends HTMLElement>(elem...
  function hasVisibleUserTurns (line 17) | function hasVisibleUserTurns(root: HTMLElement, userSelectors: string[])...
  function resolveConversationRoot (line 25) | function resolveConversationRoot({

FILE: src/pages/content/export/conversationMenuInjection.ts
  type ConversationMenuExportOptions (line 6) | type ConversationMenuExportOptions = {
  type ConversationMenuType (line 12) | type ConversationMenuType = 'top' | 'sidebar';
  type ConversationMenuContext (line 14) | type ConversationMenuContext = {
  type ResponseMenuContext (line 19) | type ResponseMenuContext = {
  constant MENU_BUTTON_CLASS (line 23) | const MENU_BUTTON_CLASS = 'gv-export-conversation-menu-btn';
  constant RESPONSE_MENU_BUTTON_CLASS (line 24) | const RESPONSE_MENU_BUTTON_CLASS = 'gv-export-response-menu-btn';
  constant MENU_PANEL_SELECTOR (line 25) | const MENU_PANEL_SELECTOR = '.mat-mdc-menu-panel[role="menu"]';
  constant SIDEBAR_CONTAINER_SELECTOR (line 26) | const SIDEBAR_CONTAINER_SELECTOR = '[data-test-id="overflow-container"]';
  constant EXPANDED_MENU_TRIGGER_SELECTOR (line 27) | const EXPANDED_MENU_TRIGGER_SELECTOR = '[aria-haspopup="menu"][aria-expa...
  constant RESPONSE_MORE_MENU_TRIGGER_TEST_ID (line 28) | const RESPONSE_MORE_MENU_TRIGGER_TEST_ID = 'more-menu-button';
  function findMenuContent (line 30) | function findMenuContent(menuPanel: HTMLElement): HTMLElement | null {
  function parseControlledIds (line 34) | function parseControlledIds(trigger: HTMLElement): string[] {
  function resolveExpandedMenuTrigger (line 44) | function resolveExpandedMenuTrigger(menuPanel: HTMLElement): HTMLElement...
  function isSidebarConversationTrigger (line 59) | function isSidebarConversationTrigger(trigger: HTMLElement): boolean {
  function hasDeepResearchReportMarkers (line 63) | function hasDeepResearchReportMarkers(menuContent: HTMLElement): boolean {
  function updateButtonLabelAndTooltip (line 71) | function updateButtonLabelAndTooltip(
  function findMenuButtonByIcon (line 79) | function findMenuButtonByIcon(
  function closeMenuOverlay (line 97) | function closeMenuOverlay(menuPanel: HTMLElement): void {
  function createMenuItemButton (line 110) | function createMenuItemButton({
  function isConversationMenuPanel (line 146) | function isConversationMenuPanel(menuPanel: HTMLElement): boolean {
  function getConversationMenuContext (line 169) | function getConversationMenuContext(menuPanel: HTMLElement): Conversatio...
  function isResponseMenuTrigger (line 178) | function isResponseMenuTrigger(trigger: HTMLElement | null): boolean {
  function isResponseMenuPanel (line 182) | function isResponseMenuPanel(menuPanel: HTMLElement): boolean {
  function getResponseMenuContext (line 200) | function getResponseMenuContext(menuPanel: HTMLElement): ResponseMenuCon...
  function injectConversationMenuExportButton (line 205) | function injectConversationMenuExportButton(
  function injectResponseMenuExportButton (line 244) | function injectResponseMenuExportButton(

FILE: src/pages/content/export/index.ts
  constant SESSION_KEY_PENDING_EXPORT (line 35) | const SESSION_KEY_PENDING_EXPORT = 'gv_export_pending';
  constant CONVERSATION_MENU_SELECTOR (line 36) | const CONVERSATION_MENU_SELECTOR = '.mat-mdc-menu-panel[role="menu"]';
  constant CONVERSATION_MENU_TRIGGER_TEST_ID (line 37) | const CONVERSATION_MENU_TRIGGER_TEST_ID = 'actions-menu-button';
  constant RESPONSE_MENU_TRIGGER_TEST_ID (line 38) | const RESPONSE_MENU_TRIGGER_TEST_ID = 'more-menu-button';
  constant MENU_INJECTION_RETRY_LIMIT (line 39) | const MENU_INJECTION_RETRY_LIMIT = 8;
  constant MENU_INJECTION_RETRY_DELAY_MS (line 40) | const MENU_INJECTION_RETRY_DELAY_MS = 80;
  constant EXPORT_PRELOAD_WAIT_OPTIONS (line 41) | const EXPORT_PRELOAD_WAIT_OPTIONS = {
  constant FINAL_EXPORT_PREPARE_DELAY_MS (line 48) | const FINAL_EXPORT_PREPARE_DELAY_MS = 120;
  type PendingExportState (line 52) | interface PendingExportState {
  function hashString (line 62) | function hashString(input: string): string {
  function isExportFormat (line 71) | function isExportFormat(value: unknown): value is ExportFormat {
  function waitForElement (line 75) | function waitForElement(selector: string, timeoutMs: number = 6000): Pro...
  function waitForAnyElement (line 101) | function waitForAnyElement(
  function normalizeText (line 139) | function normalizeText(text: string | null): string {
  function queryOutsideThoughts (line 157) | function queryOutsideThoughts<T extends Element = Element>(
  function filterTopLevel (line 170) | function filterTopLevel(elements: Element[]): HTMLElement[] {
  function getConversationRoot (line 189) | function getConversationRoot(userSelectors: string[]): HTMLElement {
  function computeConversationId (line 193) | function computeConversationId(): string {
  function getUserSelectors (line 198) | function getUserSelectors(): string[] {
  function getAssistantSelectors (line 224) | function getAssistantSelectors(): string[] {
  function dedupeByTextAndOffset (line 240) | function dedupeByTextAndOffset(elements: HTMLElement[], firstTurnOffset:...
  function ensureTurnId (line 253) | function ensureTurnId(el: Element, index: number): string {
  function readStarredSet (line 266) | function readStarredSet(): Set<string> {
  function extractAssistantText (line 279) | function extractAssistantText(el: HTMLElement): string {
  type ChatTurn (line 328) | type ChatTurn = {
  function collectChatPairs (line 338) | function collectChatPairs(): ChatTurn[] {
  type ExportMessageRole (line 428) | type ExportMessageRole = 'user' | 'assistant';
  type ExportMessage (line 430) | type ExportMessage = {
  function buildExportMessagesFromPairs (line 439) | function buildExportMessagesFromPairs(pairs: ChatTurn[]): ExportMessage[] {
  function computeSortedMessages (line 468) | function computeSortedMessages(pairsInput: ChatTurn[]): Array<ExportMess...
  function buildTurnsForSelectedMessages (line 482) | function buildTurnsForSelectedMessages(
  function buildTurnsForSelectedMessageIds (line 504) | function buildTurnsForSelectedMessageIds(
  function resolveAssistantMessageIdFromMenuTrigger (line 515) | function resolveAssistantMessageIdFromMenuTrigger(trigger: HTMLElement |...
  function ensureDropdownInjected (line 538) | function ensureDropdownInjected(logoElement: Element): HTMLButtonElement...
  function loadDictionaries (line 585) | async function loadDictionaries(): Promise<Record<AppLanguage, Record<st...
  function isMeaningfulConversationTitle (line 634) | function isMeaningfulConversationTitle(title: string | null | undefined)...
  function extractConversationIdFromUrl (line 650) | function extractConversationIdFromUrl(): string | null {
  function extractConversationIdFromHref (line 658) | function extractConversationIdFromHref(href: string): string | null {
  function isGemLabel (line 672) | function isGemLabel(text: string | null | undefined): boolean {
  function extractTitleFromLinkText (line 677) | function extractTitleFromLinkText(link?: HTMLAnchorElement | null): stri...
  function extractTitleFromConversationElement (line 691) | function extractTitleFromConversationElement(conversationEl: HTMLElement...
  function extractTitleFromNativeSidebarByConversationId (line 738) | function extractTitleFromNativeSidebarByConversationId(conversationId: s...
  function escapeCssAttributeValue (line 759) | function escapeCssAttributeValue(value: string): string {
  function getConversationTitleForExport (line 767) | function getConversationTitleForExport(): string {
  function findSidebarConversationLinkById (line 837) | function findSidebarConversationLinkById(conversationId: string): HTMLAn...
  function triggerNativeClick (line 858) | function triggerNativeClick(target: HTMLElement): void {
  function waitForConversationUrl (line 866) | async function waitForConversationUrl(
  function navigateToConversationAndWait (line 878) | async function navigateToConversationAndWait(
  function exportFromSidebarConversationTrigger (line 903) | async function exportFromSidebarConversationTrigger(
  function normalizeLang (line 923) | function normalizeLang(lang: string | undefined): AppLanguage {
  function getLanguage (line 927) | async function getLanguage(): Promise<AppLanguage> {
  function getTopUserElement (line 969) | function getTopUserElement(selectors: string[]): HTMLElement | null {
  function isElementVisibleForAlignment (line 979) | function isElementVisibleForAlignment(el: HTMLElement): boolean {
  function isLikelySidebarElement (line 991) | function isLikelySidebarElement(el: HTMLElement): boolean {
  function pickBestVisibleAlignmentTarget (line 1016) | function pickBestVisibleAlignmentTarget(
  function resolveConversationCanvasCenterX (line 1052) | function resolveConversationCanvasCenterX(): number {
  function alignElementToConversationTitleCenter (line 1113) | function alignElementToConversationTitleCenter(element: HTMLElement): ()...
  function executeExportSequence (line 1148) | async function executeExportSequence(
  function executeExportSequenceWithProgress (line 1244) | async function executeExportSequenceWithProgress(
  function performFinalExport (line 1264) | async function performFinalExport(
  function showExportProgressOverlay (line 1674) | function showExportProgressOverlay(t: (key: TranslationKey) => string): ...
  function checkPendingExport (line 1710) | async function checkPendingExport() {
  function getConversationMenuPanelsFromNode (line 1761) | function getConversationMenuPanelsFromNode(node: HTMLElement): HTMLEleme...
  function parseMenuTriggerPanelIds (line 1770) | function parseMenuTriggerPanelIds(trigger: HTMLElement): string[] {
  type ResponseCopyImageTexts (line 1780) | type ResponseCopyImageTexts = {
  function getResponseCopyImageTexts (line 1789) | function getResponseCopyImageTexts(lang: AppLanguage): ResponseCopyImage...
  function buildResponseImageFilename (line 1822) | function buildResponseImageFilename(): string {
  function isUnsupportedClipboardError (line 1827) | function isUnsupportedClipboardError(error: unknown): boolean {
  function handleResponseCopyImageClick (line 1851) | async function handleResponseCopyImageClick(
  function applyResponseActionCopyImageButtons (line 1904) | function applyResponseActionCopyImageButtons(getCurrentLanguage: () => A...
  function setupResponseActionCopyImageObserver (line 1915) | function setupResponseActionCopyImageObserver({
  function setupConversationMenuExportObserver (line 1954) | function setupConversationMenuExportObserver({
  function startExportButton (line 2093) | async function startExportButton(): Promise<void> {
  function showExportDialog (line 2282) | async function showExportDialog(

FILE: src/pages/content/export/responseActionImageButton.ts
  type ResponseActionCopyImageOptions (line 1) | type ResponseActionCopyImageOptions = {
  constant COPY_BUTTON_TEST_ID (line 7) | const COPY_BUTTON_TEST_ID = 'copy-button';
  constant MORE_BUTTON_TEST_ID (line 8) | const MORE_BUTTON_TEST_ID = 'more-menu-button';
  constant COPY_IMAGE_BUTTON_TEST_ID (line 9) | const COPY_IMAGE_BUTTON_TEST_ID = 'gv-copy-image-button';
  constant COPY_IMAGE_ICON_NAME (line 10) | const COPY_IMAGE_ICON_NAME = 'image';
  constant ASSISTANT_SCOPE_SELECTOR (line 11) | const ASSISTANT_SCOPE_SELECTOR = [
  type BoundCopyImageButton (line 23) | type BoundCopyImageButton = HTMLElement & {
  function updateButtonLabelAndTooltip (line 27) | function updateButtonLabelAndTooltip(
  function updateButtonIcon (line 46) | function updateButtonIcon(button: HTMLElement): void {
  function findButtonByTestId (line 56) | function findButtonByTestId(container: HTMLElement, testId: string): HTM...
  function findActionContainerFromMoreButton (line 62) | function findActionContainerFromMoreButton(moreButton: HTMLElement): HTM...
  function bindButtonClick (line 78) | function bindButtonClick(
  function isAssistantActionContainer (line 102) | function isAssistantActionContainer(container: HTMLElement): boolean {
  function ensureButtonPosition (line 106) | function ensureButtonPosition(
  function injectIntoActionContainer (line 121) | function injectIntoActionContainer(
  function injectResponseActionCopyImageButtons (line 152) | function injectResponseActionCopyImageButtons(

FILE: src/pages/content/export/responseImageCopy.ts
  type ClipboardWriteLike (line 3) | type ClipboardWriteLike = Pick<Clipboard, 'write'>;
  type ClipboardItemLike (line 4) | type ClipboardItemLike = new (items: Record<string, Blob>) => ClipboardI...
  type CopyElementAsImageOptions (line 6) | type CopyElementAsImageOptions = {
  function resolveClipboardDependencies (line 11) | function resolveClipboardDependencies(options?: CopyElementAsImageOption...
  function copyImageBlobToClipboard (line 23) | async function copyImageBlobToClipboard(
  function downloadImageBlob (line 36) | function downloadImageBlob(blob: Blob, filename: string): void {
  function copyElementAsImageToClipboard (line 53) | async function copyElementAsImageToClipboard(

FILE: src/pages/content/export/selectionUtils.ts
  function filterItemsBySelectedIds (line 1) | function filterItemsBySelectedIds<T>(
  function selectBelowIds (line 12) | function selectBelowIds(allIds: readonly string[], startId: string): Set...
  type SelectableMessageRole (line 23) | type SelectableMessageRole = 'user' | 'assistant';
  type SelectedMessageForTurnGrouping (line 25) | interface SelectedMessageForTurnGrouping<TElement = HTMLElement> {
  type GroupedSelectedTurn (line 33) | interface GroupedSelectedTurn<TElement = HTMLElement> {
  function parseMessageId (line 40) | function parseMessageId(messageId: string): {
  function groupSelectedMessagesByTurn (line 54) | function groupSelectedMessagesByTurn<TElement = HTMLElement>(
  function findSelectionStartIdAtLine (line 83) | function findSelectionStartIdAtLine(
  function resolveInitialSelectedMessageIds (line 102) | function resolveInitialSelectedMessageIds(

FILE: src/pages/content/export/sidebarConversationTarget.ts
  type SidebarConversationTarget (line 1) | type SidebarConversationTarget = {
  type ConversationCandidate (line 7) | type ConversationCandidate = SidebarConversationTarget & {
  constant SIDEBAR_CONVERSATION_SELECTOR (line 11) | const SIDEBAR_CONVERSATION_SELECTOR = '[data-test-id="conversation"]';
  function normalizeText (line 13) | function normalizeText(value: string | null | undefined): string {
  function normalizeConversationId (line 19) | function normalizeConversationId(value: string | null | undefined): stri...
  function extractConversationIdFromPath (line 25) | function extractConversationIdFromPath(pathname: string): string | null {
  function extractConversationIdFromHref (line 33) | function extractConversationIdFromHref(href: string | null | undefined):...
  function extractConversationIdFromJslog (line 43) | function extractConversationIdFromJslog(value: string | null | undefined...
  function parseTitleHintFromAriaLabel (line 49) | function parseTitleHintFromAriaLabel(label: string | null | undefined): ...
  function readTitleFromConversationScope (line 55) | function readTitleFromConversationScope(scope: HTMLElement): string | nu...
  function getCandidateFromScope (line 72) | function getCandidateFromScope(scope: HTMLElement): ConversationCandidat...
  function collectConversationCandidates (line 96) | function collectConversationCandidates(): ConversationCandidate[] {
  function pickNearestByVerticalDistance (line 108) | function pickNearestByVerticalDistance(
  function resolveSidebarConversationTarget (line 130) | function resolveSidebarConversationTarget(

FILE: src/pages/content/export/sidebarExportResume.ts
  constant SESSION_KEY_PENDING_SIDEBAR_EXPORT (line 1) | const SESSION_KEY_PENDING_SIDEBAR_EXPORT = 'gv_sidebar_export_pending';
  constant MAX_PENDING_AGE_MS (line 2) | const MAX_PENDING_AGE_MS = 60_000;
  type PendingSidebarExportIntent (line 4) | interface PendingSidebarExportIntent {
  function clearPendingSidebarExportIntent (line 9) | function clearPendingSidebarExportIntent(): void {
  function persistPendingSidebarExportIntent (line 13) | function persistPendingSidebarExportIntent(
  function consumePendingSidebarExportIntent (line 25) | function consumePendingSidebarExportIntent(

FILE: src/pages/content/export/topNodePreload.ts
  type Fingerprint (line 1) | type Fingerprint = {
  function hashString (line 6) | function hashString(input: string): string {
  function normalizeText (line 15) | function normalizeText(text: string | null): string {
  function filterTopLevel (line 25) | function filterTopLevel(elements: Element[], selector: string): HTMLElem...
  function computeConversationFingerprint (line 35) | function computeConversationFingerprint(
  type WaitForConversationChangeOptions (line 53) | type WaitForConversationChangeOptions = {
  type WaitForConversationChangeResult (line 61) | type WaitForConversationChangeResult = {
  constant DEFAULT_OPTIONS (line 66) | const DEFAULT_OPTIONS: WaitForConversationChangeOptions = {
  function waitForConversationFingerprintChangeOrTimeout (line 74) | async function waitForConversationFingerprintChangeOrTimeout(

FILE: src/pages/content/folder/__tests__/aistudio.test.ts
  type DragDataTransferMock (line 7) | type DragDataTransferMock = {
  function createPromptRow (line 13) | function createPromptRow(
  type AIStudioManagerInternals (line 37) | type AIStudioManagerInternals = {

FILE: src/pages/content/folder/__tests__/conversationSort.test.ts
  function createConversation (line 6) | function createConversation(

FILE: src/pages/content/folder/__tests__/folderNameInteraction.test.ts
  type TestableManager (line 12) | type TestableManager = {
  function createFolder (line 24) | function createFolder(): Folder {

FILE: src/pages/content/folder/__tests__/moveToFolderMenuItem.test.ts
  function createTemplateButton (line 5) | function createTemplateButton(): HTMLButtonElement {

FILE: src/pages/content/folder/aistudio.ts
  function waitForElement (line 18) | function waitForElement<T extends Element = Element>(
  function normalizeText (line 48) | function normalizeText(text: string | null | undefined): string {
  function downloadJSON (line 58) | function downloadJSON(data: unknown, filename: string): void {
  function now (line 76) | function now(): number {
  function uid (line 80) | function uid(): string {
  constant NOTIFICATION_TIMEOUT_MS (line 84) | const NOTIFICATION_TIMEOUT_MS = 5000;
  constant PROMPT_LINK_SELECTOR (line 85) | const PROMPT_LINK_SELECTOR = 'a.prompt-link[href^="/prompts/"]';
  constant UNBOUND_PROMPT_LINK_SELECTOR (line 86) | const UNBOUND_PROMPT_LINK_SELECTOR = `${PROMPT_LINK_SELECTOR}:not([data-...
  constant PROMPT_LIST_BIND_DEBOUNCE_MS (line 87) | const PROMPT_LIST_BIND_DEBOUNCE_MS = 120;
  constant PROMPT_TITLE_SYNC_DEBOUNCE_MS (line 88) | const PROMPT_TITLE_SYNC_DEBOUNCE_MS = 280;
  constant PROMPT_DRAG_HOST_SELECTORS (line 89) | const PROMPT_DRAG_HOST_SELECTORS = [
  function nodeContainsPromptLink (line 96) | function nodeContainsPromptLink(node: Node): boolean {
  function mutationAddsPromptLinks (line 102) | function mutationAddsPromptLinks(mutations: MutationRecord[]): boolean {
  function mutationMayAffectPromptTitles (line 111) | function mutationMayAffectPromptTitles(mutations: MutationRecord[]): boo...
  function extractPromptIdFromHref (line 140) | function extractPromptIdFromHref(rawHref: string): string | null {
  function normalizeDroppedUrl (line 154) | function normalizeDroppedUrl(raw: string): string | null {
  function parseDragDataPayload (line 164) | function parseDragDataPayload(raw: string): DragData | null {
  function validateFolderData (line 202) | function validateFolderData(data: unknown): boolean {
  class AIStudioFolderManager (line 208) | class AIStudioFolderManager {
    method createIcon (line 232) | private createIcon(name: string): HTMLSpanElement {
    method init (line 242) | async init(): Promise<void> {
    method migrateFromSyncToLocal (line 307) | private async migrateFromSyncToLocal(): Promise<void> {
    method mergeFolderData (line 342) | private mergeFolderData(local: FolderData, sync: FolderData): FolderDa...
    method cloneFolderData (line 372) | private cloneFolderData(data: FolderData): FolderData {
    method migrateLegacyFolderDataToScopedStorage (line 383) | private async migrateLegacyFolderDataToScopedStorage(): Promise<Folder...
    method toSyncAccountScope (line 407) | private toSyncAccountScope(scope: AccountScope | null): SyncAccountSco...
    method buildAccountContextFingerprint (line 416) | private buildAccountContextFingerprint(routeUserId: string | null, ema...
    method loadAccountIsolationSetting (line 420) | private async loadAccountIsolationSetting(): Promise<void> {
    method refreshAccountScope (line 436) | private async refreshAccountScope(force: boolean = false): Promise<boo...
    method handleAccountIsolationToggle (line 473) | private async handleAccountIsolationToggle(enabled: boolean): Promise<...
    method setupAccountContextPoller (line 484) | private setupAccountContextPoller(): void {
    method refreshScopedDataOnAccountContextChange (line 501) | private async refreshScopedDataOnAccountContextChange(): Promise<void> {
    method setupMessageListener (line 520) | private setupMessageListener(): void {
    method initializeFolderUI (line 556) | private async initializeFolderUI(): Promise<void> {
    method load (line 598) | private async load(): Promise<void> {
    method save (line 626) | private async save(): Promise<void> {
    method injectUI (line 643) | private injectUI(): void {
    method injectStyles (line 720) | private injectStyles(): void {
    method render (line 803) | private render(): void {
    method getCurrentPromptIdFromLocation (line 853) | private getCurrentPromptIdFromLocation(): string | null {
    method highlightActiveConversation (line 862) | private highlightActiveConversation(): void {
    method installRouteChangeListener (line 874) | private installRouteChangeListener(): void {
    method renderFolder (line 912) | private renderFolder(folder: Folder, level: number = 0): HTMLElement {
    method renderConversation (line 1003) | private renderConversation(folderId: string, conv: ConversationReferen...
    method openFolderMenu (line 1066) | private openFolderMenu(ev: MouseEvent, folderId: string): void {
    method createFolder (line 1127) | private async createFolder(parentId: string | null = null): Promise<vo...
    method renameFolder (line 1144) | private async renameFolder(folderId: string): Promise<void> {
    method deleteFolder (line 1155) | private async deleteFolder(folderId: string): Promise<void> {
    method removeConversationFromFolder (line 1175) | private removeConversationFromFolder(folderId: string, conversationId:...
    method confirmRemoveConversation (line 1181) | private confirmRemoveConversation(
    method bindDropZone (line 1259) | private bindDropZone(el: HTMLElement, targetFolderId: string | null): ...
    method observePromptList (line 1343) | private observePromptList(): void {
    method schedulePromptListBinding (line 1396) | private schedulePromptListBinding(): void {
    method schedulePromptTitleSync (line 1404) | private schedulePromptTitleSync(): void {
    method runPromptTitleSync (line 1414) | private async runPromptTitleSync(): Promise<void> {
    method hasStoredConversations (line 1425) | private hasStoredConversations(): boolean {
    method extractPromptTitle (line 1431) | private extractPromptTitle(anchor: HTMLAnchorElement | null): string |...
    method getPromptTitleFromNative (line 1446) | private getPromptTitleFromNative(conversationId: string): string | null {
    method syncConversationTitlesFromPromptList (line 1462) | private async syncConversationTitlesFromPromptList(): Promise<void> {
    method resolvePromptAnchorFromHost (line 1483) | private resolvePromptAnchorFromHost(hostEl: HTMLElement): HTMLAnchorEl...
    method resolvePromptAnchorFromDragEvent (line 1490) | private resolvePromptAnchorFromDragEvent(
    method resolvePromptDragHost (line 1502) | private resolvePromptDragHost(anchor: HTMLAnchorElement): HTMLElement {
    method setPromptDragData (line 1510) | private setPromptDragData(e: DragEvent, data: DragData, dragImageEl: H...
    method parseDragDataFromEvent (line 1528) | private parseDragDataFromEvent(event: DragEvent): DragData | null {
    method bindDraggablesInPromptList (line 1549) | private bindDraggablesInPromptList(scope: ParentNode | null = this.his...
    method observeLibraryTable (line 1584) | private observeLibraryTable(): void {
    method bindDraggablesInLibraryTable (line 1625) | private bindDraggablesInLibraryTable(): void {
    method injectLibraryDropZone (line 1674) | private injectLibraryDropZone(): void {
    method extractPromptId (line 1970) | private extractPromptId(anchor: HTMLAnchorElement): string {
    method navigateToPrompt (line 1988) | private navigateToPrompt(promptId: string, url: string): void {
    method handleExport (line 2006) | private handleExport(): void {
    method handleImport (line 2015) | private handleImport(): void {
    method timestamp (line 2061) | private timestamp(): string {
    method loadFolderEnabledSetting (line 2067) | private async loadFolderEnabledSetting(): Promise<void> {
    method setupStorageListener (line 2077) | private setupStorageListener(): void {
    method applyFolderEnabledSetting (line 2111) | private applyFolderEnabledSetting(): void {
    method attemptDataRecovery (line 2134) | private attemptDataRecovery(_error: unknown): void {
    method showErrorNotification (line 2165) | private showErrorNotification(message: string): void {
    method showNotification (line 2172) | private showNotification(message: string, level: 'info' | 'warning' | ...
    method isExtensionContextValid (line 2221) | private isExtensionContextValid(): boolean {
    method loadSidebarWidth (line 2233) | private async loadSidebarWidth(): Promise<void> {
    method saveSidebarWidth (line 2279) | private async saveSidebarWidth(): Promise<void> {
    method applySidebarWidth (line 2303) | private applySidebarWidth(force: boolean = false): void {
    method addResizeHandle (line 2328) | private addResizeHandle(): void {
    method handleCloudUpload (line 2467) | private async handleCloudUpload(): Promise<void> {
    method handleCloudSync (line 2518) | private async handleCloudSync(): Promise<void> {
    method mergePromptsData (line 2608) | private mergePromptsData(local: PromptItem[], cloud: PromptItem[]): Pr...
    method getCloudUploadTooltip (line 2638) | private async getCloudUploadTooltip(): Promise<string> {
    method getCloudSyncTooltip (line 2660) | private async getCloudSyncTooltip(): Promise<string> {
    method formatRelativeTime (line 2682) | private formatRelativeTime(timestamp: number | null): string {
  function startAIStudioFolderManager (line 2704) | async function startAIStudioFolderManager(): Promise<void> {

FILE: src/pages/content/folder/conversationSort.ts
  function getConversationSortTime (line 3) | function getConversationSortTime(conversation: ConversationReference): n...
  function sortConversationsByPriority (line 7) | function sortConversationsByPriority(

FILE: src/pages/content/folder/folderColors.ts
  type FolderColorConfig (line 14) | interface FolderColorConfig {
  constant FOLDER_COLORS (line 42) | const FOLDER_COLORS: FolderColorConfig[] = [
  function getFolderColor (line 100) | function getFolderColor(colorId: string | undefined, isDarkMode: boolean...
  function getFolderColorConfig (line 129) | function getFolderColorConfig(colorId: string): FolderColorConfig | unde...
  function isDarkMode (line 137) | function isDarkMode(): boolean {

FILE: src/pages/content/folder/gemConfig.ts
  type GemConfig (line 13) | interface GemConfig {
  constant GEM_CONFIG (line 33) | const GEM_CONFIG: GemConfig[] = [
  constant DEFAULT_GEM_ICON (line 84) | const DEFAULT_GEM_ICON = 'stars';
  constant DEFAULT_CONVERSATION_ICON (line 89) | const DEFAULT_CONVERSATION_ICON = 'chat_bubble';
  function getGemConfig (line 94) | function getGemConfig(gemId: string): GemConfig | undefined {
  function getGemIdFromIcon (line 101) | function getGemIdFromIcon(iconName: string): string | undefined {
  function getGemIcon (line 109) | function getGemIcon(gemId: string): string {
  function isKnownGem (line 117) | function isKnownGem(gemId: string): boolean {
  function getAllGemIcons (line 124) | function getAllGemIcons(): string[] {
  function createIconToGemMap (line 131) | function createIconToGemMap(): Record<string, string> {

FILE: src/pages/content/folder/index.ts
  function startFolderManager (line 3) | async function startFolderManager(): Promise<FolderManager | null> {

FILE: src/pages/content/folder/manager.ts
  constant STORAGE_KEY (line 30) | const STORAGE_KEY = 'gvFolderData';
  constant IS_DEBUG (line 31) | const IS_DEBUG = false;
  constant ROOT_CONVERSATIONS_ID (line 32) | const ROOT_CONVERSATIONS_ID = '__root_conversations__';
  constant NOTIFICATION_TIMEOUT_MS (line 33) | const NOTIFICATION_TIMEOUT_MS = 10000;
  constant FOLDER_TREE_INDENT_MIN (line 34) | const FOLDER_TREE_INDENT_MIN = -8;
  constant FOLDER_TREE_INDENT_MAX (line 35) | const FOLDER_TREE_INDENT_MAX = 32;
  constant FOLDER_TREE_INDENT_DEFAULT (line 36) | const FOLDER_TREE_INDENT_DEFAULT = -8;
  constant FOLDER_NAME_SINGLE_CLICK_DELAY_MS (line 37) | const FOLDER_NAME_SINGLE_CLICK_DELAY_MS = 220;
  constant SESSION_BACKUP_KEY (line 40) | const SESSION_BACKUP_KEY = 'gvFolderBackup';
  constant SESSION_BACKUP_TIMESTAMP_KEY (line 41) | const SESSION_BACKUP_TIMESTAMP_KEY = 'gvFolderBackupTimestamp';
  function clampFolderTreeIndent (line 43) | function clampFolderTreeIndent(value: unknown): number {
  function calculateFolderHeaderPaddingLeft (line 49) | function calculateFolderHeaderPaddingLeft(level: number, indent: number)...
  function calculateFolderConversationPaddingLeft (line 53) | function calculateFolderConversationPaddingLeft(level: number, indent: n...
  function calculateFolderDialogPaddingLeft (line 57) | function calculateFolderDialogPaddingLeft(level: number, indent: number)...
  function validateFolderData (line 64) | function validateFolderData(data: unknown): boolean {
  class FolderManager (line 70) | class FolderManager {
    method debug (line 71) | private debug(...args: unknown[]): void {
    method debugWarn (line 77) | private debugWarn(...args: unknown[]): void {
    method isDebugEnabled (line 82) | private isDebugEnabled(): boolean {
    method constructor (line 153) | constructor() {
    method init (line 171) | async init(): Promise<void> {
    method destroy (line 237) | destroy(): void {
    method addCleanupTask (line 339) | private addCleanupTask(task: () => void): void {
    method initializeFolderUI (line 343) | private async initializeFolderUI(): Promise<void> {
    method waitForSidebar (line 421) | private async waitForSidebar(): Promise<void> {
    method findRecentSection (line 437) | private findRecentSection(): void {
    method createFolderUI (line 478) | private createFolderUI(): void {
    method createMultiSelectIndicator (line 509) | private createMultiSelectIndicator(): HTMLElement {
    method createHeader (line 624) | private createHeader(): HTMLElement {
    method createFoldersList (line 711) | private createFoldersList(): HTMLElement {
    method createFolderElement (line 756) | private createFolderElement(folder: Folder, level = 0): HTMLElement {
    method clearPendingFolderNameClick (line 883) | private clearPendingFolderNameClick(): void {
    method handleFolderNameClick (line 889) | private handleFolderNameClick(folderId: string, event: MouseEvent): vo...
    method handleFolderNameDoubleClick (line 903) | private handleFolderNameDoubleClick(folderId: string): void {
    method createConversationElement (line 908) | private createConversationElement(
    method setupDropZone (line 1116) | private setupDropZone(element: HTMLElement, folderId: string): void {
    method setupRootDropZone (line 1182) | private setupRootDropZone(element: HTMLElement): void {
    method makeConversationsDraggable (line 1254) | private makeConversationsDraggable(): void {
    method canFolderBeDragged (line 1283) | private canFolderBeDragged(folderId: string): boolean {
    method applyFolderDraggableBehavior (line 1297) | private applyFolderDraggableBehavior(element: HTMLElement, folder: Fol...
    method enableFolderDragging (line 1315) | private enableFolderDragging(element: HTMLElement, folder: Folder): vo...
    method disableFolderDragging (line 1377) | private disableFolderDragging(element: HTMLElement): void {
    method makeConversationDraggable (line 1404) | private makeConversationDraggable(element: HTMLElement): void {
    method findConversationElement (line 1581) | private findConversationElement(conversationId: string): HTMLElement |...
    method extractConversationId (line 1602) | private extractConversationId(element: HTMLElement): string {
    method extractConversationData (line 1654) | private extractConversationData(element: HTMLElement): {
    method extractConversationIdFromElement (line 1742) | private extractConversationIdFromElement(element: Element): string | u...
    method setupMutationObserver (line 1766) | private setupMutationObserver(): void {
    method setupSideNavObserver (line 1877) | private setupSideNavObserver(): void {
    method updateVisibilityBasedOnSideNav (line 1904) | private updateVisibilityBasedOnSideNav(): void {
    method reinitializeFolderUI (line 1945) | private reinitializeFolderUI(): void {
    method createFolder (line 2015) | private createFolder(parentId: string | null = null): void {
    method renameFolder (line 2103) | private renameFolder(folderId: string): void {
    method deleteFolder (line 2177) | private deleteFolder(folderId: string, _event?: MouseEvent): void {
    method getFolderAndDescendants (line 2261) | private getFolderAndDescendants(folderId: string): string[] {
    method toggleFolder (line 2270) | private toggleFolder(folderId: string): void {
    method togglePinFolder (line 2280) | private togglePinFolder(folderId: string): void {
    method sortFolders (line 2293) | private sortFolders(folders: Folder[]): Folder[] {
    method sortConversations (line 2312) | private sortConversations(conversations: ConversationReference[]): Con...
    method setupConversationReorderZone (line 2320) | private setupConversationReorderZone(
    method createReorderGap (line 2405) | private createReorderGap(
    method reorderFolder (line 2485) | private reorderFolder(folderId: string, targetParentId: string, insert...
    method ensureConversationsInFolder (line 2522) | private ensureConversationsInFolder(folderId: string, dragData: DragDa...
    method reorderOrMoveConversations (line 2573) | private reorderOrMoveConversations(
    method addConversationToFolder (line 2667) | private addConversationToFolder(
    method addConversationsToFolder (line 2722) | private addConversationsToFolder(
    method addFolderToFolder (line 2789) | private addFolderToFolder(targetFolderId: string, dragData: DragData):...
    method moveFolderToRoot (line 2822) | private moveFolderToRoot(dragData: DragData): void {
    method isFolderDescendant (line 2846) | private isFolderDescendant(folderId: string, potentialAncestorId: stri...
    method toggleConversationStar (line 2859) | private toggleConversationStar(folderId: string, conversationId: strin...
    method confirmRemoveConversation (line 2878) | private confirmRemoveConversation(
    method removeConversationFromFolder (line 2945) | private removeConversationFromFolder(folderId: string, conversationId:...
    method batchDeleteConversations (line 2956) | private batchDeleteConversations(): void {
    method batchDeleteNativeConversations (line 2987) | private async batchDeleteNativeConversations(): Promise<void> {
    method triggerNativeDeleteForConversation (line 3071) | private async triggerNativeDeleteForConversation(conversationId: strin...
    method findNativeConversationElement (line 3118) | private findNativeConversationElement(conversationId: string): HTMLEle...
    method findAndClickMoreButton (line 3138) | private async findAndClickMoreButton(conversationEl: HTMLElement): Pro...
    method waitForDeleteButtonAndClick (line 3182) | private async waitForDeleteButtonAndClick(): Promise<boolean> {
    method confirmDeleteIfNeeded (line 3257) | private async confirmDeleteIfNeeded(): Promise<void> {
    method getDeleteKeywords (line 3347) | private getDeleteKeywords(): string[] {
    method isVisibleElement (line 3358) | private isVisibleElement(el: HTMLElement): boolean {
    method clickBackdropToCloseMenu (line 3372) | private clickBackdropToCloseMenu(): void {
    method showBatchDeleteProgress (line 3383) | private showBatchDeleteProgress(current: number, total: number): void {
    method updateBatchDeleteProgress (line 3444) | private updateBatchDeleteProgress(current: number, total: number): void {
    method hideBatchDeleteProgress (line 3460) | private hideBatchDeleteProgress(): void {
    method delay (line 3470) | private delay(ms: number): Promise<void> {
    method clearSelection (line 3475) | private clearSelection(): void {
    method selectConversation (line 3479) | private selectConversation(conversationId: string): void {
    method toggleConversationSelection (line 3483) | private toggleConversationSelection(conversationId: string): void {
    method updateConversationSelectionUI (line 3506) | private updateConversationSelectionUI(): void {
    method enterMultiSelectMode (line 3543) | private enterMultiSelectMode(
    method exitMultiSelectMode (line 3570) | private exitMultiSelectMode(): void {
    method setupOutsideClickHandler (line 3595) | private setupOutsideClickHandler(): void {
    method removeOutsideClickHandler (line 3625) | private removeOutsideClickHandler(): void {
    method cleanupSelectionArtifacts (line 3632) | private cleanupSelectionArtifacts(): void {
    method showInvalidSelectionFeedback (line 3661) | private showInvalidSelectionFeedback(element: HTMLElement): void {
    method updateMultiSelectModeUI (line 3687) | private updateMultiSelectModeUI(): void {
    method getSelectedConversationsData (line 3742) | private getSelectedConversationsData(_folderId: string): ConversationR...
    method renameConversation (line 3760) | private renameConversation(
    method showFolderMenu (line 3854) | private showFolderMenu(event: MouseEvent, folderId: string): void {
    method showColorPicker (line 3906) | private showColorPicker(
    method changeFolderColor (line 4051) | private changeFolderColor(folderId: string, colorId: string): void {
    method showMoveToFolderDialog (line 4062) | private showMoveToFolderDialog(
    method moveConversationToFolder (line 4154) | private moveConversationToFolder(
    method addConversationToFolderFromNative (line 4188) | private addConversationToFolderFromNative(
    method setupNativeConversationMenuObserver (line 4222) | private setupNativeConversationMenuObserver(): void {
    method isConversationMenu (line 4274) | private isConversationMenu(menuElement: HTMLElement): boolean {
    method injectMoveToFolderButton (line 4320) | private injectMoveToFolderButton(menuContent: HTMLElement): void {
    method findConversationElementFromMenu (line 4427) | private findConversationElementFromMenu(): HTMLElement | null {
    method setupConversationClickTracking (line 4446) | private setupConversationClickTracking(): void {
    method extractNativeConversationId (line 4584) | private extractNativeConversationId(conversationEl: HTMLElement): stri...
    method extractNativeConversationTitle (line 4646) | private extractNativeConversationTitle(conversationEl: HTMLElement): s...
    method syncConversationTitleFromNative (line 4692) | private syncConversationTitleFromNative(conversationId: string): strin...
    method updateConversationTitle (line 4726) | private updateConversationTitle(conversationId: string, newTitle: stri...
    method scheduleConversationRemovalCheck (line 4756) | private scheduleConversationRemovalCheck(conversationId: string): void {
    method cancelPendingRemovalForElement (line 4778) | private cancelPendingRemovalForElement(element: HTMLElement): void {
    method isConversationInDOM (line 4797) | private isConversationInDOM(conversationId: string): boolean {
    method getCurrentConversationId (line 4836) | private getCurrentConversationId(): string | null {
    method confirmConversationRemoval (line 4847) | private confirmConversationRemoval(conversationId: string): void {
    method removeConversationFromAllFolders (line 4883) | private removeConversationFromAllFolders(conversationId: string): void {
    method extractHexIdFromJslog (line 4909) | private extractHexIdFromJslog(scope: HTMLElement): string | null {
    method extractHexIdFromMenu (line 4941) | private extractHexIdFromMenu(menuContent: HTMLElement): string | null {
    method buildConversationUrlFromId (line 4960) | private buildConversationUrlFromId(hexId: string): string {
    method extractFallbackTitle (line 4974) | private extractFallbackTitle(conversationEl: HTMLElement): string | nu...
    method isGemLabel (line 5014) | private isGemLabel(text: string): boolean {
    method extractTitleFromLinkText (line 5027) | private extractTitleFromLinkText(link?: HTMLAnchorElement | null): str...
    method extractNativeConversationUrl (line 5045) | private extractNativeConversationUrl(conversationEl: HTMLElement): str...
    method refresh (line 5070) | private refresh(): void {
    method getCurrentHexIdFromLocation (line 5105) | private getCurrentHexIdFromLocation(): string | null {
    method highlightActiveConversationInFolders (line 5117) | private highlightActiveConversationInFolders(): void {
    method ensureDataIntegrity (line 5133) | private ensureDataIntegrity(): void {
    method ensureSortIndices (line 5180) | private ensureSortIndices(): void {
    method loadData (line 5224) | private async loadData(): Promise<void> {
    method cloneFolderData (line 5275) | private cloneFolderData(data: FolderData): FolderData {
    method filterLegacyFolderDataByCurrentAccount (line 5286) | private filterLegacyFolderDataByCurrentAccount(data: FolderData): Fold...
    method migrateLegacyFolderDataToScopedStorage (line 5341) | private async migrateLegacyFolderDataToScopedStorage(): Promise<Folder...
    method attemptDataRecovery (line 5370) | private attemptDataRecovery(error: unknown): void {
    method showDataLossNotification (line 5404) | private showDataLossNotification(): void {
    method showNotificationByLevel (line 5415) | private showNotificationByLevel(
    method saveData (line 5466) | private async saveData(): Promise<boolean> {
    method loadFolderEnabledSetting (line 5525) | private async loadFolderEnabledSetting(): Promise<void> {
    method loadAccountIsolationSetting (line 5536) | private async loadAccountIsolationSetting(): Promise<void> {
    method refreshAccountScope (line 5549) | private async refreshAccountScope(): Promise<void> {
    method toSyncAccountScope (line 5573) | private toSyncAccountScope(scope: AccountScope | null): SyncAccountSco...
    method loadHideArchivedSetting (line 5582) | private async loadHideArchivedSetting(): Promise<void> {
    method loadFilterUserSetting (line 5595) | private async loadFilterUserSetting(): Promise<void> {
    method loadFolderTreeIndentSetting (line 5608) | private async loadFolderTreeIndentSetting(): Promise<void> {
    method applyFolderTreeIndentSetting (line 5621) | private applyFolderTreeIndentSetting(value: unknown): void {
    method handleAccountIsolationToggle (line 5633) | private async handleAccountIsolationToggle(enabled: boolean): Promise<...
    method setupStorageListener (line 5645) | private setupStorageListener(): void {
    method reloadFoldersFromStorage (line 5711) | private async reloadFoldersFromStorage(): Promise<void> {
    method performMigration (line 5724) | private async performMigration(): Promise<void> {
    method mergeFolderDataForAutoSync (line 5740) | private mergeFolderDataForAutoSync(local: FolderData, cloud: FolderDat...
    method applyFolderEnabledSetting (line 5792) | private applyFolderEnabledSetting(): void {
    method applyHideArchivedSetting (line 5814) | private applyHideArchivedSetting(): void {
    method applyHideArchivedToConversation (line 5826) | private applyHideArchivedToConversation(conv: HTMLElement): void {
    method isConversationInFolders (line 5837) | private isConversationInFolders(conversationId: string): boolean {
    method generateId (line 5865) | private generateId(): string {
    method hashString (line 5869) | private hashString(str: string): string {
    method navigateToConversationById (line 5879) | private navigateToConversationById(folderId: string, conversationId: s...
    method isSameConversation (line 5900) | private isSameConversation(targetId: string, conversation: Conversatio...
    method markConversationAsRecentlyOpened (line 5913) | private markConversationAsRecentlyOpened(conversationId: string): void {
    method navigateToConversation (line 5940) | private navigateToConversation(url: string, conversation?: Conversatio...
    method checkAndUpdateGemId (line 6028) | private checkAndUpdateGemId(hexId: string): void {
    method renderAllFolders (line 6073) | private renderAllFolders(): void {
    method reloadScopedDataOnAccountRouteChange (line 6092) | private async reloadScopedDataOnAccountRouteChange(): Promise<void> {
    method installRouteChangeListener (line 6107) | private installRouteChangeListener(): void {
    method installSidebarClickListener (line 6186) | private installSidebarClickListener(): void {
    method t (line 6208) | private t(key: string): string {
    method updateHeaderLanguageText (line 6216) | private updateHeaderLanguageText(): void {
    method setupMessageListener (line 6265) | private setupMessageListener(): void {
    method collectAllSidebarConversations (line 6323) | private collectAllSidebarConversations(): Array<{
    method createTooltip (line 6345) | private createTooltip(): void {
    method showTooltip (line 6351) | private showTooltip(element: HTMLElement, text: string): void {
    method hideTooltip (line 6393) | private hideTooltip(): void {
    method exportFolders (line 6404) | private exportFolders(): void {
    method showImportDialog (line 6436) | private showImportDialog(): void {
    method createRadioOption (line 6572) | private createRadioOption(value: string, label: string, checked: boole...
    method handleImport (line 6591) | private async handleImport(fileInput: HTMLInputElement, strategy: Impo...
    method handleImportFromText (line 6691) | private async handleImportFromText(jsonText: string, strategy: ImportS...
    method hasVisibleContent (line 6781) | private hasVisibleContent(folderId: string): boolean {
    method filterConversationsByCurrentUser (line 6802) | private filterConversationsByCurrentUser(
    method getCurrentUserId (line 6824) | private getCurrentUserId(): string {
    method getUserIdFromUrl (line 6839) | private getUserIdFromUrl(url: string): string | null {
    method toggleFilterCurrentUser (line 6852) | private toggleFilterCurrentUser(): void {
    method showNotification (line 6882) | private showNotification(message: string, type: 'success' | 'error' | ...
    method showImportExportMenu (line 6904) | private showImportExportMenu(event: MouseEvent): void {
    method handleCloudUpload (line 6955) | private async handleCloudUpload(): Promise<void> {
    method handleCloudSync (line 7006) | private async handleCloudSync(): Promise<void> {
    method mergePrompts (line 7114) | private mergePrompts(local: PromptItem[], cloud: PromptItem[]): Prompt...
    method mergeStarredMessages (line 7144) | private mergeStarredMessages(
    method mergeFolderData (line 7196) | private mergeFolderData(local: FolderData, cloud: FolderData): FolderD...
    method getCloudUploadTooltip (line 7232) | private async getCloudUploadTooltip(): Promise<string> {
    method getCloudSyncTooltip (line 7254) | private async getCloudSyncTooltip(): Promise<string> {
    method formatRelativeTime (line 7276) | private formatRelativeTime(timestamp: number | null): string {

FILE: src/pages/content/folder/moveToFolderMenuItem.ts
  function createMoveToFolderMenuItemFallback (line 3) | function createMoveToFolderMenuItemFallback(label: string): HTMLButtonEl...
  function createMoveToFolderMenuItem (line 35) | function createMoveToFolderMenuItem(

FILE: src/pages/content/folder/storage/FolderStorageAdapter.ts
  type IFolderStorageAdapter (line 27) | interface IFolderStorageAdapter {
  class LocalStorageFolderAdapter (line 65) | class LocalStorageFolderAdapter implements IFolderStorageAdapter {
    method init (line 70) | async init(key: string): Promise<void> {
    method loadData (line 88) | async loadData(key: string): Promise<FolderData | null> {
    method saveData (line 111) | async saveData(key: string, data: FolderData): Promise<boolean> {
    method removeData (line 139) | async removeData(key: string): Promise<void> {
    method getBackendName (line 147) | getBackendName(): string {
  class SafariFolderAdapter (line 162) | class SafariFolderAdapter implements IFolderStorageAdapter {
    method init (line 167) | async init(key: string): Promise<void> {
    method loadData (line 171) | async loadData(key: string): Promise<FolderData | null> {
    method saveData (line 184) | async saveData(key: string, data: FolderData): Promise<boolean> {
    method removeData (line 202) | async removeData(key: string): Promise<void> {
    method getBackendName (line 210) | getBackendName(): string {
    method migrateFromLocalStorage (line 218) | async migrateFromLocalStorage(key: string): Promise<boolean> {
  function createFolderStorageAdapter (line 238) | function createFolderStorageAdapter(): IFolderStorageAdapter {

FILE: src/pages/content/folder/types.ts
  type Folder (line 1) | interface Folder {
  type ConversationReference (line 13) | interface ConversationReference {
  type FolderData (line 27) | interface FolderData {
  type DragData (line 33) | interface DragData {

FILE: src/pages/content/folderSpacing/__tests__/folderSpacing.test.ts
  constant STYLE_ID (line 3) | const STYLE_ID = 'gv-folder-spacing-style';
  constant GEMINI_KEY (line 4) | const GEMINI_KEY = 'gvFolderSpacing';
  constant AISTUDIO_KEY (line 5) | const AISTUDIO_KEY = 'gvAIStudioFolderSpacing';

FILE: src/pages/content/folderSpacing/index.ts
  type FolderSpacingPlatform (line 13) | type FolderSpacingPlatform = 'gemini' | 'aistudio';
  constant STYLE_ID (line 15) | const STYLE_ID = 'gv-folder-spacing-style';
  constant STORAGE_KEYS (line 16) | const STORAGE_KEYS: Record<FolderSpacingPlatform, string> = {
  constant DEFAULT_SPACING (line 20) | const DEFAULT_SPACING = 2;
  constant MIN_SPACING (line 21) | const MIN_SPACING = 0;
  constant MAX_SPACING (line 22) | const MAX_SPACING = 16;
  function clamp (line 24) | function clamp(value: number): number {
  function applyGeminiSpacing (line 29) | function applyGeminiSpacing(clamped: number, style: HTMLStyleElement) {
  function applyAIStudioSpacing (line 52) | function applyAIStudioSpacing(clamped: number, style: HTMLStyleElement) {
  function applySpacing (line 78) | function applySpacing(spacing: number, platform: FolderSpacingPlatform) {
  function removeStyles (line 95) | function removeStyles() {
  function startFolderSpacingAdjuster (line 104) | function startFolderSpacingAdjuster(platform: FolderSpacingPlatform = 'g...

FILE: src/pages/content/fork/ForkNodesService.ts
  function sendMessage (line 8) | async function sendMessage<T>(type: string, payload?: unknown): Promise<...
  class ForkNodesService (line 28) | class ForkNodesService {
    method addForkNode (line 29) | static async addForkNode(node: ForkNode): Promise<boolean> {
    method removeForkNode (line 41) | static async removeForkNode(
    method getAllForkNodes (line 57) | static async getAllForkNodes(): Promise<ForkNodesData> {
    method getForConversation (line 62) | static async getForConversation(conversationId: string): Promise<ForkN...
    method getGroup (line 70) | static async getGroup(forkGroupId: string): Promise<ForkNode[]> {

FILE: src/pages/content/fork/__tests__/ForkNodesService.test.ts
  function createForkNode (line 7) | function createForkNode(overrides: Partial<ForkNode> = {}): ForkNode {

FILE: src/pages/content/fork/__tests__/branching.test.ts
  function createNode (line 6) | function createNode(overrides: Partial<ForkNode> = {}): ForkNode {

FILE: src/pages/content/fork/branching.ts
  type ForkPlan (line 4) | interface ForkPlan {
  function resolveForkPlan (line 10) | function resolveForkPlan(
  function buildBranchDisplayNodes (line 55) | function buildBranchDisplayNodes(groupNodesList: ForkNode[][]): ForkNode...

FILE: src/pages/content/fork/chatPairs.ts
  type ForkChatPair (line 9) | interface ForkChatPair {
  function normalizeText (line 16) | function normalizeText(text: string | null): string {
  function queryOutsideThoughts (line 21) | function queryOutsideThoughts<T extends Element = Element>(
  function filterTopLevel (line 34) | function filterTopLevel(elements: Element[]): HTMLElement[] {
  function dedupeByTextAndOffset (line 53) | function dedupeByTextAndOffset(elements: HTMLElement[], firstTurnOffset:...
  function getUserSelectors (line 66) | function getUserSelectors(): string[] {
  function getAssistantSelectors (line 95) | function getAssistantSelectors(): string[] {
  function pickAssistantExportElement (line 109) | function pickAssistantExportElement(assistantHost: HTMLElement): HTMLEle...
  function collectForkChatPairs (line 123) | function collectForkChatPairs(): ForkChatPair[] {

FILE: src/pages/content/fork/featureFlag.ts
  function isForkFeatureEnabledValue (line 1) | function isForkFeatureEnabledValue(value: unknown): boolean {

FILE: src/pages/content/fork/forkContext.ts
  type ForkLanguage (line 1) | type ForkLanguage = 'en' | 'ar' | 'es' | 'fr' | 'ja' | 'ko' | 'pt' | 'ru...
  function normalizeLanguage (line 3) | function normalizeLanguage(raw: string | undefined): ForkLanguage {
  constant CONTEXT_PREFIX (line 18) | const CONTEXT_PREFIX: Record<ForkLanguage, string> = {
  function composeForkInputWithContext (line 81) | function composeForkInputWithContext(historyMarkdown: string, rawLanguag...

FILE: src/pages/content/fork/forkTypes.ts
  type ForkNode (line 5) | interface ForkNode {
  type ForkNodesData (line 22) | interface ForkNodesData {

FILE: src/pages/content/fork/index.ts
  constant STYLE_ID (line 28) | const STYLE_ID = 'gemini-voyager-fork-style';
  constant FORK_BTN_CLASS (line 29) | const FORK_BTN_CLASS = 'gv-fork-btn';
  constant FORK_CONFIRM_CLASS (line 30) | const FORK_CONFIRM_CLASS = 'gv-fork-confirm';
  constant FORK_INDICATOR_CLASS (line 31) | const FORK_INDICATOR_CLASS = 'gv-fork-indicator';
  constant FORK_INDICATOR_GROUP_CLASS (line 32) | const FORK_INDICATOR_GROUP_CLASS = 'gv-fork-indicator-group';
  constant FORK_INDICATOR_ITEM_CLASS (line 33) | const FORK_INDICATOR_ITEM_CLASS = 'gv-fork-indicator-item';
  constant FORK_INDICATOR_DELETE_CLASS (line 34) | const FORK_INDICATOR_DELETE_CLASS = 'gv-fork-indicator-delete';
  constant PENDING_FORK_KEY (line 35) | const PENDING_FORK_KEY = 'gvPendingFork';
  constant FORK_ICON (line 37) | const FORK_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="14" he...
  constant OBSERVER_DEBOUNCE_MS (line 39) | const OBSERVER_DEBOUNCE_MS = 500;
  constant CONVERSATION_VERIFY_TIMEOUT_MS (line 40) | const CONVERSATION_VERIFY_TIMEOUT_MS = 4000;
  constant CONVERSATION_EXISTENCE_CACHE_TTL_MS (line 41) | const CONVERSATION_EXISTENCE_CACHE_TTL_MS = 30000;
  function injectStyles (line 49) | function injectStyles(): void {
  function extractConversationIdFromUrl (line 271) | function extractConversationIdFromUrl(): string | null {
  function getConversationTitle (line 278) | function getConversationTitle(): string {
  function ensureTurnId (line 290) | function ensureTurnId(el: HTMLElement, index: number): string {
  function resolveUserMessageHost (line 299) | function resolveUserMessageHost(userEl: HTMLElement): HTMLElement {
  function findUserCopyButtonAnchor (line 307) | function findUserCopyButtonAnchor(userEl: HTMLElement): HTMLElement | nu...
  function resolveForkButtonHost (line 320) | function resolveForkButtonHost(userEl: HTMLElement): HTMLElement {
  function extractConversationIdFromHref (line 324) | function extractConversationIdFromHref(href: string): string | null {
  function findSidebarConversationLinkById (line 336) | function findSidebarConversationLinkById(conversationId: string): HTMLAn...
  function triggerNativeClick (line 346) | function triggerNativeClick(target: HTMLElement): void {
  function navigateToForkConversation (line 354) | function navigateToForkConversation(node: ForkNode): void {
  function collectSidebarConversationIds (line 371) | function collectSidebarConversationIds(): Set<string> {
  function getPreferredLanguage (line 381) | async function getPreferredLanguage(): Promise<string | undefined> {
  function checkConversationExists (line 401) | async function checkConversationExists(
  function pruneDeletedNodesFromGroup (line 445) | async function pruneDeletedNodesFromGroup(
  function clearInjectedForkIndicators (line 470) | function clearInjectedForkIndicators(): void {
  function hasOrDedupForkIndicatorGroup (line 474) | function hasOrDedupForkIndicatorGroup(hostEl: HTMLElement): boolean {
  function extractConversationUpToTurn (line 491) | function extractConversationUpToTurn(userTurnIndex: number, sourceTurnId...
  function dismissConfirm (line 519) | function dismissConfirm(): void {
  function onDocumentClick (line 526) | function onDocumentClick(e: MouseEvent): void {
  function scheduleForkIndicatorRefresh (line 532) | function scheduleForkIndicatorRefresh(): void {
  function injectForkButtons (line 541) | function injectForkButtons(): void {
  function showForkConfirmation (line 575) | function showForkConfirmation(btn: HTMLElement, userEl: HTMLElement, tur...
  function executeFork (line 617) | async function executeFork(userEl: HTMLElement, turnIndex: number): Prom...
  type PendingForkData (line 700) | interface PendingForkData {
  constant PENDING_FORK_STALE_MS (line 712) | const PENDING_FORK_STALE_MS = 60000;
  function readPendingFork (line 714) | async function readPendingFork(): Promise<PendingForkData | null> {
  function checkAndHandlePendingFork (line 754) | function checkAndHandlePendingFork(): void {
  function handlePendingForkFromStorage (line 769) | async function handlePendingForkFromStorage(): Promise<void> {
  function waitForElement (line 810) | function waitForElement(selector: string, timeoutMs: number): Promise<HT...
  function watchForNewConversation (line 835) | function watchForNewConversation(pendingFork: PendingForkData): void {
  function injectForkIndicators (line 911) | async function injectForkIndicators(): Promise<void> {
  function updateForkButtonTexts (line 1039) | function updateForkButtonTexts(): void {
  function startFork (line 1069) | function startFork(): () => void {

FILE: src/pages/content/fork/markdown.ts
  type ForkExtractedTurn (line 1) | interface ForkExtractedTurn {
  function buildForkMarkdown (line 6) | function buildForkMarkdown(

FILE: src/pages/content/fork/turnId.ts
  constant USER_TURN_ID_RE (line 1) | const USER_TURN_ID_RE = /^u-(\d+)(?:-.+)?$/;
  function makeStableTurnId (line 3) | function makeStableTurnId(index: number): string {
  function normalizeTurnId (line 7) | function normalizeTurnId(turnId: string): string {

FILE: src/pages/content/gemsHider/index.ts
  constant STYLE_ID (line 16) | const STYLE_ID = 'gv-gems-hider-style';
  constant HIDDEN_CLASS (line 17) | const HIDDEN_CLASS = 'gv-gems-hidden';
  constant PEEK_BAR_CLASS (line 18) | const PEEK_BAR_CLASS = 'gv-gems-peek-bar';
  constant TOGGLE_BTN_CLASS (line 19) | const TOGGLE_BTN_CLASS = 'gv-gems-toggle-btn';
  constant STORAGE_KEY (line 20) | const STORAGE_KEY = 'gvGemsHidden';
  constant GEMS_CONTAINER_SELECTOR (line 23) | const GEMS_CONTAINER_SELECTOR = '.gems-list-container';
  constant ARROW_ICON_SELECTOR (line 24) | const ARROW_ICON_SELECTOR = '[data-test-id="arrow-icon"]';
  function injectStyles (line 32) | function injectStyles(): void {
  function createToggleButton (line 223) | function createToggleButton(): HTMLButtonElement {
  function createPeekBar (line 242) | function createPeekBar(): HTMLDivElement {
  function getHiddenState (line 256) | async function getHiddenState(): Promise<boolean> {
  function setHiddenState (line 272) | async function setHiddenState(hidden: boolean): Promise<void> {
  function applyState (line 287) | function applyState(gemsEl: HTMLElement, peekBar: HTMLDivElement, hidden...
  function setupGemsHider (line 300) | async function setupGemsHider(containerEl: HTMLElement): Promise<void> {
  function updateLanguageText (line 348) | function updateLanguageText(): void {
  function initGemsHider (line 367) | function initGemsHider(): void {
  function cleanup (line 406) | function cleanup(): void {
  function startGemsHider (line 428) | function startGemsHider(): () => void {

FILE: src/pages/content/index.tsx
  constant HEAVY_FEATURE_INIT_DELAY (line 62) | const HEAVY_FEATURE_INIT_DELAY = 100;
  constant LIGHT_FEATURE_INIT_DELAY (line 63) | const LIGHT_FEATURE_INIT_DELAY = 50;
  constant BACKGROUND_TAB_MIN_DELAY (line 64) | const BACKGROUND_TAB_MIN_DELAY = 3000;
  constant BACKGROUND_TAB_MAX_DELAY (line 65) | const BACKGROUND_TAB_MAX_DELAY = 8000;
  function isForkFeatureEnabled (line 76) | async function isForkFeatureEnabled(): Promise<boolean> {
  function isCustomWebsite (line 88) | async function isCustomWebsite(): Promise<boolean> {
  function initializeFeatures (line 126) | async function initializeFeatures(): Promise<void> {
  function getInitializationDelay (line 320) | function getInitializationDelay(): number {
  function handleVisibilityChange (line 342) | function handleVisibilityChange(): void {

FILE: src/pages/content/inputCollapse/__tests__/inputCollapse.test.ts
  function createMockContainer (line 65) | async function createMockContainer(content: string = ''): Promise<HTMLEl...

FILE: src/pages/content/inputCollapse/index.ts
  constant STYLE_ID (line 7) | const STYLE_ID = 'gemini-voyager-input-collapse';
  constant COLLAPSED_CLASS (line 8) | const COLLAPSED_CLASS = 'gv-input-collapsed';
  constant PLACEHOLDER_CLASS (line 9) | const PLACEHOLDER_CLASS = 'gv-collapse-placeholder';
  function isHomepageOrNewConversation (line 23) | function isHomepageOrNewConversation(): boolean {
  function isGemsEditorPage (line 34) | function isGemsEditorPage(): boolean {
  function shouldDisableAutoCollapse (line 43) | function shouldDisableAutoCollapse(): boolean {
  function injectStyles (line 50) | function injectStyles() {
  function getInputContainer (line 173) | function getInputContainer(): HTMLElement | null {
  function expandInputCollapseIfNeeded (line 220) | function expandInputCollapseIfNeeded(): void {
  function expandInputWithCursorAtEnd (line 229) | function expandInputWithCursorAtEnd(): void {
  function collapseInput (line 239) | function collapseInput(): void {
  function isInputEmpty (line 259) | function isInputEmpty(container: HTMLElement): boolean {
  function ensurePlaceholder (line 280) | function ensurePlaceholder(container: HTMLElement) {
  function startInputCollapse (line 299) | function startInputCollapse() {
  function cleanup (line 358) | function cleanup() {
  function initInputCollapse (line 398) | function initInputCollapse(allowCollapseNotEmpty: boolean = false) {
  function isInputRelatedElement (line 597) | function isInputRelatedElement(element: HTMLElement, container: HTMLElem...
  function expand (line 644) | function expand(container: HTMLElement, moveCursorToEnd: boolean = false) {
  function moveCursorToEndOfElement (line 668) | function moveCursorToEndOfElement(element: HTMLElement): void {
  function tryCollapse (line 683) | function tryCollapse(container: HTMLElement) {

FILE: src/pages/content/katexConfig/index.ts
  function configureKaTeX (line 16) | function configureKaTeX(): void {
  function initKaTeXConfig (line 35) | function initKaTeXConfig(): void {

FILE: src/pages/content/markdownPatcher/index.ts
  function fixBrokenBoldTags (line 13) | function fixBrokenBoldTags(root: HTMLElement) {
  function startMarkdownPatcher (line 191) | function startMarkdownPatcher() {

FILE: src/pages/content/mermaid/index.ts
  constant GENERIC_LANGUAGE_LABELS (line 665) | const GENERIC_LANGUAGE_LABELS = new Set([

FILE: src/pages/content/preventAutoScroll/index.ts
  constant GV_BRIDGE_ID (line 3) | const GV_BRIDGE_ID = 'gv-prevent-auto-scroll-bridge';
  function getBridgeElement (line 5) | function getBridgeElement(): HTMLElement {
  function notifyScript (line 16) | function notifyScript(enabled: boolean): void {
  function injectScript (line 21) | function injectScript(): void {
  function startPreventAutoScroll (line 34) | async function startPreventAutoScroll(): Promise<void> {

FILE: src/pages/content/prompt/index.ts
  type PromptItem (line 35) | type PromptItem = {
  type PanelPosition (line 43) | type PanelPosition = { top: number; left: number };
  type TriggerPosition (line 44) | type TriggerPosition = { bottom: number; right: number };
  constant STORAGE_KEYS (line 46) | const STORAGE_KEYS = {
  constant LATEST_VERSION_CACHE_KEY (line 60) | const LATEST_VERSION_CACHE_KEY = 'gvLatestVersionCache';
  constant LATEST_VERSION_MAX_AGE (line 61) | const LATEST_VERSION_MAX_AGE = 1000 * 60 * 60 * 6;
  type PMTheme (line 63) | type PMTheme = 'light' | 'dark';
  function detectPageTheme (line 65) | function detectPageTheme(): PMTheme {
  function getRuntimeUrl (line 85) | function getRuntimeUrl(path: string): string {
  function safeParseJSON (line 95) | function safeParseJSON<T>(raw: string, fallback: T): T {
  function createI18n (line 105) | function createI18n() {
  function uid (line 138) | function uid(): string {
  function readStorage (line 161) | async function readStorage<T>(key: StorageKey, fallback: T): Promise<T> {
  function writeStorage (line 170) | async function writeStorage<T>(key: StorageKey, value: T): Promise<void> {
  function getLatestVersionCached (line 180) | async function getLatestVersionCached(): Promise<string | null> {
  function createEl (line 228) | function createEl<K extends keyof HTMLElementTagNameMap>(
  function elFromHTML (line 237) | function elFromHTML(html: string): HTMLElement {
  function escapeHtml (line 243) | function escapeHtml(s: string): string {
  function dedupeTags (line 252) | function dedupeTags(tags: string[]): string[] {
  function collectAllTags (line 266) | function collectAllTags(items: PromptItem[]): string[] {
  function copyText (line 272) | function copyText(text: string): Promise<void> {
  function computeAnchoredPosition (line 292) | function computeAnchoredPosition(
  function startPromptManager (line 305) | async function startPromptManager(): Promise<{ destroy: () => void }> {

FILE: src/pages/content/prompt/scrollHint.ts
  type ScrollHintState (line 1) | type ScrollHintState = {
  constant DEFAULT_TOLERANCE_PX (line 6) | const DEFAULT_TOLERANCE_PX = 4;
  function getScrollHintState (line 8) | function getScrollHintState(

FILE: src/pages/content/quoteReply/__tests__/quoteReply.test.ts
  function selectSourceText (line 16) | function selectSourceText(start = 0, end = 5) {
  function triggerQuoteReply (line 30) | function triggerQuoteReply() {

FILE: src/pages/content/quoteReply/index.ts
  constant CSS_CLASSES (line 14) | const CSS_CLASSES = {
  constant TIMING (line 20) | const TIMING = {
  constant POSITIONING (line 30) | const POSITIONING = {
  constant QUOTE_ICON (line 38) | const QUOTE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="16" h...
  constant STYLE_ID (line 40) | const STYLE_ID = 'gemini-voyager-quote-reply-style';
  function injectStyles (line 42) | function injectStyles() {
  function getChatInput (line 109) | function getChatInput(): HTMLElement | null {
  function countLineBreaks (line 134) | function countLineBreaks(raw: string): number {
  type SeparatorInsertResult (line 138) | interface SeparatorInsertResult {
  function getContenteditableQuoteSeparator (line 143) | function getContenteditableQuoteSeparator(): string {
  function getPlaceholderCandidates (line 149) | function getPlaceholderCandidates(input: HTMLElement): string[] {
  function isChatInputEmpty (line 163) | function isChatInputEmpty(input: HTMLElement | HTMLTextAreaElement): boo...
  function tryInsertQuoteSeparator (line 195) | function tryInsertQuoteSeparator(input: HTMLElement, separator: string):...
  function replaceMathWithLatex (line 224) | function replaceMathWithLatex(root: DocumentFragment): void {
  function extractTextWithLatex (line 252) | function extractTextWithLatex(range: Range): string {
  function startQuoteReply (line 277) | function startQuoteReply() {

FILE: src/pages/content/recentsHider/index.ts
  constant STYLE_ID (line 16) | const STYLE_ID = 'gv-recents-hider-style';
  constant HIDDEN_CLASS (line 17) | const HIDDEN_CLASS = 'gv-recents-hidden';
  constant PEEK_BAR_CLASS (line 18) | const PEEK_BAR_CLASS = 'gv-recents-peek-bar';
  constant TOGGLE_BTN_CLASS (line 19) | const TOGGLE_BTN_CLASS = 'gv-recents-toggle-btn';
  constant STORAGE_KEY (line 20) | const STORAGE_KEY = 'gvRecentsHidden';
  constant RECENTS_SELECTOR (line 23) | const RECENTS_SELECTOR = '.my-stuff-recents-preview';
  function injectStyles (line 31) | function injectStyles(): void {
  function createToggleButton (line 224) | function createToggleButton(): HTMLButtonElement {
  function createPeekBar (line 243) | function createPeekBar(): HTMLDivElement {
  function getHiddenState (line 257) | async function getHiddenState(): Promise<boolean> {
  function setHiddenState (line 273) | async function setHiddenState(hidden: boolean): Promise<void> {
  function applyState (line 288) | function applyState(recentsEl: HTMLElement, peekBar: HTMLDivElement, hid...
  function setupRecentsHider (line 301) | async function setupRecentsHider(recentsEl: HTMLElement): Promise<void> {
  function updateLanguageText (line 345) | function updateLanguageText(): void {
  function initRecentsHider (line 364) | function initRecentsHider(): void {
  function cleanup (line 403) | function cleanup(): void {
  function startRecentsHider (line 425) | function startRecentsHider(): () => void {

FILE: src/pages/content/sendBehavior/__tests__/sendBehavior.test.ts
  function markElementVisible (line 5) | function markElementVisible(element: HTMLElement): void {
  function fireCtrlEnter (line 12) | function fireCtrlEnter(target: HTMLElement): KeyboardEvent {

FILE: src/pages/content/sendBehavior/index.ts
  constant SEND_BUTTON_SELECTORS (line 25) | const SEND_BUTTON_SELECTORS = [
  constant EDITABLE_SELECTORS (line 41) | const EDITABLE_SELECTORS = '[contenteditable="true"], [role="textbox"], ...
  constant LOG_PREFIX (line 44) | const LOG_PREFIX = '[SendBehavior]';
  function findSendButton (line 71) | function findSendButton(inputElement: HTMLElement): HTMLElement | null {
  function insertNewlineInContentEditable (line 142) | function insertNewlineInContentEditable(target: HTMLElement): void {
  function insertNewlineInTextarea (line 199) | function insertNewlineInTextarea(textarea: HTMLTextAreaElement): void {
  function handleKeyDown (line 218) | function handleKeyDown(event: KeyboardEvent): void {
  function attachToInput (line 272) | function attachToInput(element: HTMLElement): void {
  function attachToAllInputs (line 290) | function attachToAllInputs(): void {
  function setupObserver (line 303) | function setupObserver(): void {
  function disconnectObserver (line 336) | function disconnectObserver(): void {
  function enableFeature (line 350) | function enableFeature(): void {
  function disableFeature (line 363) | function disableFeature(): void {
  function loadSettings (line 385) | async function loadSettings(): Promise<boolean> {
  function setupStorageListener (line 412) | function setupStorageListener(): void {
  function cleanup (line 441) | function cleanup(): void {
  function startSendBehavior (line 464) | async function startSendBehavior(): Promise<() => void> {

FILE: src/pages/content/sendBehavior/utils.ts
  function getTextOffset (line 11) | function getTextOffset(root: HTMLElement): number | null {
  function setCaretPosition (line 29) | function setCaretPosition(root: HTMLElement, targetOffset: number): void {

FILE: src/pages/content/shared/__tests__/nativeMenuItemTemplate.test.ts
  function createTemplateButton (line 8) | function createTemplateButton(): HTMLButtonElement {
  function createMenuContent (line 33) | function createMenuContent(): HTMLElement {
  function createWrappedMenuContent (line 41) | function createWrappedMenuContent(): HTMLElement {

FILE: src/pages/content/shared/nativeMenuItemTemplate.ts
  type NativeMenuItemTemplateOptions (line 1) | type NativeMenuItemTemplateOptions = {
  function findTemplateMenuItem (line 10) | function findTemplateMenuItem(
  function updateMenuItemLabel (line 36) | function updateMenuItemLabel(button: HTMLButtonElement, label: string): ...
  function updateMenuItemIcon (line 51) | function updateMenuItemIcon(button: HTMLButtonElement, iconName: string)...
  function clearTemplateSpecificAttributes (line 68) | function clearTemplateSpecificAttributes(button: HTMLButtonElement): void {
  function createMenuItemFromNativeTemplate (line 96) | function createMenuItemFromNativeTemplate({
  function updateMenuItemTemplateLabel (line 125) | function updateMenuItemTemplateLabel(

FILE: src/pages/content/sidebarAutoHide/__tests__/SidebarAutoHide.test.ts
  function mockVisibleRect (line 3) | function mockVisibleRect(element: HTMLElement, width: number = 300, heig...

FILE: src/pages/content/sidebarAutoHide/index.ts
  constant STYLE_ID (line 14) | const STYLE_ID = 'gv-sidebar-auto-hide-style';
  constant FULL_HIDE_STYLE_ID (line 15) | const FULL_HIDE_STYLE_ID = 'gv-sidebar-full-hide-style';
  constant STORAGE_KEY (line 16) | const STORAGE_KEY = 'gvSidebarAutoHide';
  constant FULL_HIDE_STORAGE_KEY (line 17) | const FULL_HIDE_STORAGE_KEY = 'gvSidebarFullHide';
  constant EDGE_TRIGGER_ID (line 18) | const EDGE_TRIGGER_ID = 'gv-sidebar-edge-trigger';
  constant FULL_HIDE_COLLAPSED_CLASS (line 19) | const FULL_HIDE_COLLAPSED_CLASS = 'gv-sidebar-full-hide-collapsed';
  constant SIDEBAR_TOGGLE_BUTTON_SELECTOR (line 20) | const SIDEBAR_TOGGLE_BUTTON_SELECTOR =
  constant SIDEBAR_TOGGLE_BUTTON_MATCH_SELECTOR (line 22) | const SIDEBAR_TOGGLE_BUTTON_MATCH_SELECTOR = `${SIDEBAR_TOGGLE_BUTTON_SE...
  constant SIDEBAR_STATE_SYNC_DELAYS_MS (line 23) | const SIDEBAR_STATE_SYNC_DELAYS_MS = [0, 120, 360] as const;
  constant LEAVE_DELAY_MS (line 26) | const LEAVE_DELAY_MS = 500;
  constant ENTER_DELAY_MS (line 27) | const ENTER_DELAY_MS = 300;
  constant SIDENAV_CHECK_INTERVAL_MS (line 29) | const SIDENAV_CHECK_INTERVAL_MS = 1000;
  constant RESIZE_DEBOUNCE_MS (line 31) | const RESIZE_DEBOUNCE_MS = 200;
  constant MENU_CLICK_PAUSE_MS (line 33) | const MENU_CLICK_PAUSE_MS = 1500;
  constant EDGE_TRIGGER_WIDTH (line 35) | const EDGE_TRIGGER_WIDTH = 6;
  constant CUSTOM_POPUP_SELECTORS (line 36) | const CUSTOM_POPUP_SELECTORS = [
  function isElementVisible (line 66) | function isElementVisible(element: HTMLElement): boolean {
  function getTransitionStyle (line 77) | function getTransitionStyle(): string {
  function insertTransitionStyle (line 88) | function insertTransitionStyle(): void {
  function removeTransitionStyle (line 96) | function removeTransitionStyle(): void {
  function getFullHideStyle (line 103) | function getFullHideStyle(): string {
  function insertFullHideStyle (line 117) | function insertFullHideStyle(): void {
  function removeFullHideStyle (line 125) | function removeFullHideStyle(): void {
  function handleEdgeTriggerLeave (line 132) | function handleEdgeTriggerLeave(e: MouseEvent): void {
  function createEdgeTrigger (line 149) | function createEdgeTrigger(): void {
  function removeEdgeTrigger (line 169) | function removeEdgeTrigger(): void {
  function showEdgeTrigger (line 178) | function showEdgeTrigger(): void {
  function hideEdgeTrigger (line 184) | function hideEdgeTrigger(): void {
  function findToggleButton (line 192) | function findToggleButton(): HTMLButtonElement | null {
  function getSidebarContentContainer (line 204) | function getSidebarContentContainer(): HTMLElement | null {
  function getStructuredSidebarCollapsedState (line 208) | function getStructuredSidebarCollapsedState(): boolean | null {
  function isSidebarCollapsed (line 221) | function isSidebarCollapsed(): boolean {
  function isSidebarVisible (line 236) | function isSidebarVisible(): boolean {
  function isPaused (line 243) | function isPaused(): boolean {
  function clearScheduledSidebarStateSync (line 247) | function clearScheduledSidebarStateSync(): void {
  function scheduleSidebarStateSync (line 254) | function scheduleSidebarStateSync(): void {
  function pauseAutoCollapse (line 267) | function pauseAutoCollapse(durationMs: number): void {
  function isPopupOrDialogOpen (line 271) | function isPopupOrDialogOpen(): boolean {
  function isMouseOverSidebarArea (line 292) | function isMouseOverSidebarArea(): boolean {
  function handleMenuClick (line 318) | function handleMenuClick(e: Event): void {
  function clickToggleButton (line 362) | function clickToggleButton(): boolean {
  function collapseSidebar (line 380) | function collapseSidebar(): void {
  function expandSidebar (line 392) | function expandSidebar(): void {
  function handleMouseEnter (line 403) | function handleMouseEnter(): void {
  function handleMouseLeave (line 422) | function handleMouseLeave(): void {
  function getSidenavElement (line 443) | function getSidenavElement(): HTMLElement | null {
  function attachEventListeners (line 447) | function attachEventListeners(): boolean {
  function detachEventListeners (line 464) | function detachEventListeners(): void {
  function checkAndReattach (line 472) | function checkAndReattach(): void {
  function handleResize (line 499) | function handleResize(): void {
  function startSidenavCheck (line 516) | function startSidenavCheck(): void {
  function stopSidenavCheck (line 523) | function stopSidenavCheck(): void {
  function setupInfrastructure (line 532) | function setupInfrastructure(): void {
  function teardownInfrastructure (line 548) | function teardownInfrastructure(): void {
  function ensureMenuClickHandler (line 570) | function ensureMenuClickHandler(): void {
  function maybeRemoveMenuClickHandler (line 576) | function maybeRemoveMenuClickHandler(): void {
  function enable (line 584) | function enable(): void {
  function disable (line 605) | function disable(): void {
  function setFullHideCollapsedState (line 638) | function setFullHideCollapsedState(collapsed: boolean): void {
  function syncFullHideState (line 642) | function syncFullHideState(): void {
  function enableFullHide (line 656) | function enableFullHide(): void {
  function disableFullHide (line 675) | function disableFullHide(): void {
  function startSidebarAutoHide (line 695) | function startSidebarAutoHide(): void {

FILE: src/pages/content/sidebarWidth/index.ts
  constant STYLE_ID (line 2) | const STYLE_ID = 'gv-sidebar-width-style';
  constant DEFAULT_PERCENT (line 3) | const DEFAULT_PERCENT = 26;
  constant MIN_PERCENT (line 4) | const MIN_PERCENT = 15;
  constant MAX_PERCENT (line 5) | const MAX_PERCENT = 45;
  constant LEGACY_BASELINE_PX (line 6) | const LEGACY_BASELINE_PX = 1200;
  constant DEFAULT_PX (line 8) | const DEFAULT_PX = Math.round((DEFAULT_PERCENT / 100) * LEGACY_BASELINE_...
  constant MIN_PX (line 9) | const MIN_PX = Math.round((MIN_PERCENT / 100) * LEGACY_BASELINE_PX);
  constant MAX_PX (line 10) | const MAX_PX = Math.round((MAX_PERCENT / 100) * LEGACY_BASELINE_PX);
  constant SEARCH_HIT_DEBUG_THROTTLE_MS (line 11) | const SEARCH_HIT_DEBUG_THROTTLE_MS = 1200;
  function normalizeWidth (line 33) | function normalizeWidth(value: number): { normalized: number; unit: 'px'...
  function buildStyle (line 41) | function buildStyle(widthValue: number): string {
  function ensureStyleEl (line 155) | function ensureStyleEl(): HTMLStyleElement {
  function applyWidth (line 165) | function applyWidth(widthValue: number): void {
  function removeStyles (line 170) | function removeStyles(): void {
  function formatElementForDebug (line 175) | function formatElementForDebug(element: Element | null): string {
  function setupSearchButtonHitTestDebug (line 183) | function setupSearchButtonHitTestDebug(): void {
  constant ENABLED_KEY (line 231) | const ENABLED_KEY = 'gvSidebarWidthEnabled';
  function startSidebarWidthAdjuster (line 234) | function startSidebarWidthAdjuster(): void {

FILE: src/pages/content/timeline/EventBus.ts
  type EventCallback (line 6) | type EventCallback<T = unknown> = (data: T) => void;
  type EventMap (line 8) | interface EventMap {
  class EventBus (line 16) | class EventBus {
    method constructor (line 20) | private constructor() {}
    method getInstance (line 25) | static getInstance(): EventBus {
    method on (line 35) | on<K extends keyof EventMap>(event: K, callback: EventCallback<EventMa...
    method off (line 49) | off<K extends keyof EventMap>(event: K, callback: EventCallback<EventM...
    method emit (line 62) | emit<K extends keyof EventMap>(event: K, data: EventMap[K]): void {
    method clear (line 78) | clear(): void {
    method getListenerCount (line 85) | getListenerCount(event?: keyof EventMap): number {

FILE: src/pages/content/timeline/StarredMessagesService.ts
  class StarredMessagesService (line 8) | class StarredMessagesService {
    method sendMessage (line 12) | private static async sendMessage<T>(type: string, payload?: unknown): ...
    method getAllStarredMessages (line 31) | static async getAllStarredMessages(): Promise<StarredMessagesData> {
    method getStarredMessagesForConversation (line 46) | static async getStarredMessagesForConversation(
    method addStarredMessage (line 64) | static async addStarredMessage(message: StarredMessage): Promise<void> {
    method removeStarredMessage (line 89) | static async removeStarredMessage(conversationId: string, turnId: stri...
    method updateLegacyStorage (line 115) | private static updateLegacyStorage(
    method isMessageStarred (line 151) | static async isMessageStarred(conversationId: string, turnId: string):...
    method getAllStarredMessagesSorted (line 159) | static async getAllStarredMessagesSorted(): Promise<StarredMessage[]> {

FILE: src/pages/content/timeline/TimelinePreviewPanel.ts
  constant SEARCH_DEBOUNCE_MS (line 9) | const SEARCH_DEBOUNCE_MS = 200;
  constant LIST_ICON_SVG (line 11) | const LIST_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="14...
  class TimelinePreviewPanel (line 13) | class TimelinePreviewPanel {
    method constructor (line 33) | constructor(private readonly anchorElement: HTMLElement) {}
    method isOpen (line 35) | get isOpen(): boolean {
    method init (line 39) | init(
    method updateMarkers (line 51) | updateMarkers(markers: ReadonlyArray<PreviewMarkerData>): void {
    method updateActiveTurn (line 57) | updateActiveTurn(turnId: string | null): void {
    method reposition (line 66) | reposition(): void {
    method toggle (line 72) | toggle(): void {
    method open (line 80) | open(): void {
    method close (line 90) | close(): void {
    method destroy (line 103) | destroy(): void {
    method createDOM (line 137) | private createDOM(): void {
    method setupEventListeners (line 175) | private setupEventListeners(): void {
    method updateTranslatedText (line 211) | private updateTranslatedText(): void {
    method isRTLContext (line 221) | private isRTLContext(): boolean {
    method applyDirection (line 225) | private applyDirection(): void {
    method setupScrollIsolation (line 232) | private setupScrollIsolation(): void {
    method positionToggle (line 252) | private positionToggle(): void {
    method positionPanel (line 264) | private positionPanel(): void {
    method applyFilter (line 297) | private applyFilter(): void {
    method handleSearchInput (line 310) | private handleSearchInput(): void {
    method renderList (line 321) | private renderList(): void {
    method createItem (line 342) | private createItem(marker: PreviewMarkerData): HTMLElement {
    method appendHighlighted (line 386) | private appendHighlighted(container: HTMLElement, text: string, query:...
    method truncateText (line 407) | private truncateText(text: string, maxLen: number): string {
    method updateActiveHighlight (line 412) | private updateActiveHighlight(): void {
    method scrollActiveIntoView (line 421) | private scrollActiveIntoView(): void {
    method formatStarredTime (line 430) | private formatStarredTime(timestamp: number): string {
    method markersEqual (line 439) | private markersEqual(newMarkers: ReadonlyArray<PreviewMarkerData>): bo...

FILE: src/pages/content/timeline/__tests__/TimelineManagerFlowClickActiveReset.test.ts
  type TimelineMarker (line 6) | type TimelineMarker = {

FILE: src/pages/content/timeline/__tests__/TimelineManagerNavigationRefresh.test.ts
  function setElementTop (line 5) | function setElementTop(el: HTMLElement, top: number): void {

FILE: src/pages/content/timeline/__tests__/TimelineManagerPreviewPanelReposition.test.ts
  type PreviewPanelLike (line 5) | type PreviewPanelLike = {
  type TimelineManagerInternal (line 10) | type TimelineManagerInternal = {

FILE: src/pages/content/timeline/__tests__/TimelineManagerSummaryExtraction.test.ts
  function setElementTop (line 5) | function setElementTop(el: HTMLElement, top: number): void {
  type TimelineMarker (line 20) | type TimelineMarker = {
  type TimelineManagerInternal (line 25) | type TimelineManagerInternal = {
  function setupForRecalc (line 40) | function setupForRecalc(container: HTMLElement): {

FILE: src/pages/content/timeline/__tests__/TimelineManagerTooltipDirection.test.ts
  type TimelineManagerInternal (line 6) | type TimelineManagerInternal = {

FILE: src/pages/content/timeline/__tests__/TimelinePreviewPanel.test.ts
  function makeMarkers (line 27) | function makeMarkers(count: number): PreviewMarkerData[] {

FILE: src/pages/content/timeline/index.ts
  function isGeminiConversationRoute (line 3) | function isGeminiConversationRoute(pathname = location.pathname): boolean {
  type HistoryStateArgs (line 9) | type HistoryStateArgs = Parameters<History['pushState']>;
  function initializeTimeline (line 22) | function initializeTimeline(): void {
  function handleUrlChange (line 46) | function handleUrlChange(): void {
  function patchHistoryOnce (line 97) | function patchHistoryOnce(): void {
  function attachRouteListenersOnce (line 126) | function attachRouteListenersOnce(): void {
  function cleanup (line 147) | function cleanup(): void {
  function startTimeline (line 184) | function startTimeline(): void {

FILE: src/pages/content/timeline/manager.ts
  function hashString (line 14) | function hashString(input: string): string {
  constant TURN_LABEL_PREFIXES (line 24) | const TURN_LABEL_PREFIXES =
  constant VISUALLY_HIDDEN_CLASS_FRAGMENT (line 26) | const VISUALLY_HIDDEN_CLASS_FRAGMENT = 'visually-hidden';
  constant INJECTED_UI_SELECTOR (line 27) | const INJECTED_UI_SELECTOR = '.gv-fork-btn, .gv-fork-confirm, .gv-fork-i...
  type ExtGlobal (line 29) | type ExtGlobal = typeof globalThis & {
  class TimelineManager (line 59) | class TimelineManager {
    method init (line 199) | async init(): Promise<void> {
    method computeElementTopsInScrollContainer (line 371) | private computeElementTopsInScrollContainer(elements: HTMLElement[]): ...
    method updateIntersectionObserverTargetsFromMarkers (line 399) | private updateIntersectionObserverTargetsFromMarkers(): void {
    method applyContainerVisibility (line 405) | private applyContainerVisibility(): void {
    method isInResizeEdge (line 415) | private isInResizeEdge(ev: PointerEvent): boolean {
    method startResize (line 429) | private startResize(ev: PointerEvent): void {
    method saveBarWidth (line 459) | private saveBarWidth(): void {
    method computeConversationId (line 469) | private computeConversationId(): string {
    method getStarsStorageKey (line 477) | private getStarsStorageKey(): string | null {
    method safeLocalStorageGet (line 484) | private safeLocalStorageGet(key: string): string | null {
    method safeLocalStorageSet (line 496) | private safeLocalStorageSet(key: string, value: string): boolean {
    method areStarredSetsEqual (line 506) | private areStarredSetsEqual(a: Set<string>, b: Set<string>): boolean {
    method applyStarredIdSet (line 514) | private applyStarredIdSet(nextSet: Set<string>, persistLocal = true): ...
    method applySharedStarredData (line 545) | private applySharedStarredData(data?: StarredMessagesData | null): void {
    method syncStarredFromService (line 560) | private async syncStarredFromService(): Promise<void> {
    method getConversationTitle (line 579) | private getConversationTitle(): string {
    method waitForElement (line 662) | private waitForElement(selector: string, timeoutMs: number = 5000): Pr...
    method waitForAnyElement (line 689) | private waitForAnyElement(
    method findCriticalElements (line 727) | private async findCriticalElements(): Promise<boolean> {
    method getConfiguredUserTurnSelector (line 816) | private getConfiguredUserTurnSelector(): string {
    method injectTimelineUI (line 827) | private injectTimelineUI(): void {
    method updateIntersectionObserverTargets (line 925) | private updateIntersectionObserverTargets(): void {
    method normalizeText (line 934) | private normalizeText(text: string | null): string {
    method hasVisuallyHiddenClass (line 946) | private hasVisuallyHiddenClass(el: Element): boolean {
    method extractTurnText (line 954) | private extractTurnText(element: HTMLElement | null): string {
    method filterTopLevel (line 982) | private filterTopLevel(elements: Element[]): HTMLElement[] {
    method dedupeByTextAndOffset (line 1023) | private dedupeByTextAndOffset(elements: HTMLElement[], firstTurnOffset...
    method getCSSVarNumber (line 1048) | private getCSSVarNumber(el: Element, name: string, fallback: number): ...
    method getTrackPadding (line 1054) | private getTrackPadding(): number {
    method getMinGap (line 1059) | private getMinGap(): number {
    method ensureTurnId (line 1065) | private ensureTurnId(el: Element, index: number): string {
    method detectCssVarTopSupport (line 1079) | private detectCssVarTopSupport(pad: number, usableC: number): boolean {
    method updateTimelineGeometry (line 1097) | private updateTimelineGeometry(): void {
    method applyMinGapWithHidden (line 1154) | private applyMinGapWithHidden(
    method applyMinGap (line 1211) | private applyMinGap(positions: number[], minTop: number, maxTop: numbe...
    method injectMessageTimestamps (line 1336) | private async injectMessageTimestamps(): Promise<void> {
    method loadMessageTimestampsEnabledSetting (line 1401) | private async loadMessageTimestampsEnabledSetting(): Promise<void> {
    method setupObservers (line 1406) | private setupObservers(): void {
    method setupEventListeners (line 1428) | private setupEventListeners(): void {
    method smoothScrollTo (line 1750) | private smoothScrollTo(targetElement: HTMLElement, duration = 600): vo...
    method easeInOutQuad (line 1778) | private easeInOutQuad(t: number, b: number, c: number, d: number): num...
    method updateActiveDotUI (line 1808) | private updateActiveDotUI(): void {
    method clearSearchHighlights (line 1817) | private clearSearchHighlights(): void {
    method highlightSearchInDOM (line 1829) | private highlightSearchInDOM(query: string): void {
    method debounce (line 1863) | private debounce<T extends (...args: unknown[]) => void>(func: T, dela...
    method getActiveIndex (line 1871) | private getActiveIndex(): number {
    method getFlowDurationMs (line 1876) | private getFlowDurationMs(): number {
    method computeFlowDuration (line 1885) | private computeFlowDuration(fromIdx: number, toIdx: number): number {
    method ensureRunnerRing (line 1895) | private ensureRunnerRing(): void {
    method startRunner (line 1919) | private startRunner(fromIdx: number, toIdx: number, duration: number):...
    method truncateToThreeLines (line 1960) | private truncateToThreeLines(
    method computePlacementInfo (line 2002) | private computePlacementInfo(dot: HTMLElement): { placement: 'left' | ...
    method showTooltipForDot (line 2035) | private showTooltipForDot(dot: DotElement): void {
    method placeTooltipAt (line 2067) | private placeTooltipAt(
    method refreshTooltipForDot (line 2121) | private refreshTooltipForDot(dot: DotElement): void {
    method buildTooltipText (line 2133) | private buildTooltipText(dot: DotElement): string {
    method scheduleScrollSync (line 2147) | private scheduleScrollSync(): void {
    method computeActiveByScroll (line 2158) | private computeActiveByScroll(): void {
    method syncTimelineTrackToMain (line 2208) | private syncTimelineTrackToMain(): void {
    method lowerBound (line 2220) | private lowerBound(arr: number[], x: number): number {
    method upperBound (line 2230) | private upperBound(arr: number[], x: number): number {
    method updateVirtualRangeAndRender (line 2241) | private updateVirtualRangeAndRender(): void {
    method updateSlider (line 2340) | private updateSlider(): void {
    method showSlider (line 2377) | private showSlider(): void {
    method hideSliderDeferred (line 2387) | private hideSliderDeferred(): void {
    method handleSliderDrag (line 2396) | private handleSliderDrag(e: PointerEvent): void {
    method endSliderDrag (line 2418) | private endSliderDrag(_e: PointerEvent): void {
    method toggleDraggable (line 2428) | private toggleDraggable(enabled: boolean): void {
    method toggleMarkerLevel (line 2437) | private toggleMarkerLevel(enabled: boolean): void {
    method handleBarDrag (line 2448) | private handleBarDrag(e: PointerEvent): void {
    method endBarDrag (line 2456) | private endBarDrag(_e: PointerEvent): void {
    method savePosition (line 2462) | private savePosition(): void {
    method applyRTLUpdate (line 2486) | private applyRTLUpdate(language?: string | null): void {
    method applyPosition (line 2500) | private applyPosition(top: number, left: number): void {
    method reapplyPosition (line 2521) | private async reapplyPosition(): Promise<void> {
    method hideTooltip (line 2583) | private hideTooltip(immediate = false): void {
    method toggleStar (line 2596) | private async toggleStar(turnId: string): Promise<void> {
    method saveStars (line 2653) | private saveStars(): void {
    method loadStars (line 2662) | private async loadStars(): Promise<void> {
    method getLevelsStorageKey (line 2682) | private getLevelsStorageKey(): string | null {
    method loadMarkerLevels (line 2687) | private loadMarkerLevels(): void {
    method saveMarkerLevels (line 2710) | private saveMarkerLevels(): void {
    method getCollapsedStorageKey (line 2724) | private getCollapsedStorageKey(): string | null {
    method loadCollapsedMarkers (line 2728) | private loadCollapsedMarkers(): void {
    method saveCollapsedMarkers (line 2746) | private saveCollapsedMarkers(): void {
    method isMarkerCollapsed (line 2752) | private isMarkerCollapsed(turnId: string): boolean {
    method toggleCollapse (line 2756) | private toggleCollapse(turnId: string): void {
    method getHiddenMarkerIndices (line 2767) | private getHiddenMarkerIndices(): Set<number> {
    method calculateEffectiveBaseN (line 2802) | private calculateEffectiveBaseN(markerIndex: number, _hiddenIndices: S...
    method calculateCollapsedPositions (line 2837) | private calculateCollapsedPositions(
    method canCollapseMarker (line 2887) | private canCollapseMarker(turnId: string): boolean {
    method getMarkerLevel (line 2900) | private getMarkerLevel(turnId: string): MarkerLevel {
    method setMarkerLevel (line 2904) | private setMarkerLevel(turnId: string, level: MarkerLevel): void {
    method showContextMenu (line 2921) | private showContextMenu(dot: DotElement, x: number, y: number): void {
    method hideContextMenu (line 3035) | private hideContextMenu(): void {
    method cancelLongPress (line 3042) | private cancelLongPress(): void {
    method initKeyboardShortcuts (line 3058) | private async initKeyboardShortcuts(): Promise<void> {
    method enqueueNavigation (line 3078) | private enqueueNavigation(direction: 'previous' | 'next', isRepeat: bo...
    method canEnqueueNavigation (line 3096) | private canEnqueueNavigation(direction: 'previous' | 'next'): boolean {
    method shouldAttemptRefreshForNavigation (line 3112) | private shouldAttemptRefreshForNavigation(): boolean {
    method getScrollContainerForElement (line 3123) | private getScrollContainerForElement(element: HTMLElement): HTMLElement {
    method shouldRefreshForInteraction (line 3140) | private shouldRefreshForInteraction(targetElement: HTMLElement | null)...
    method maybeRefreshMarkersForInteraction (line 3159) | private maybeRefreshMarkersForInteraction(targetElement: HTMLElement |...
    method processNavigationQueue (line 3173) | private async processNavigationQueue(): Promise<void> {
    method performNodeNavigation (line 3197) | private async performNodeNavigation(targetIndex: number, currentIndex:...
    method navigateToPreviousNode (line 3231) | private async navigateToPreviousNode(): Promise<void> {
    method navigateToNextNode (line 3244) | private async navigateToNextNode(): Promise<void> {
    method maybeRefreshMarkersForNavigation (line 3254) | private maybeRefreshMarkersForNavigation(direction: 'previous' | 'next...
    method refreshCriticalElementsFromDocument (line 3273) | private refreshCriticalElementsFromDocument(): boolean {
    method handleStarredMessageNavigation (line 3327) | private handleStarredMessageNavigation(): void {
    method destroy (line 3393) | destroy(): void {

FILE: src/pages/content/timeline/starredTypes.ts
  type StarredMessage (line 5) | interface StarredMessage {
  type StarredMessagesData (line 20) | interface StarredMessagesData {

FILE: src/pages/content/timeline/types.ts
  type DotElement (line 1) | type DotElement = HTMLButtonElement & {
  type MarkerLevel (line 8) | type MarkerLevel = 1 | 2 | 3;
  type PreviewMarkerData (line 10) | interface PreviewMarkerData {

FILE: src/pages/content/timestamp/TimestampService.ts
  type TimestampMap (line 8) | interface TimestampMap {
  class TimestampService (line 12) | class TimestampService {
    method constructor (line 20) | constructor(private storageService: IStorageService = StorageFactory.c...
    method initialize (line 22) | async initialize(): Promise<void> {
    method recordTimestamp (line 31) | async recordTimestamp(turnId: TurnId, timestamp?: number): Promise<voi...
    method getTimestamp (line 37) | getTimestamp(turnId: TurnId): number | null {
    method formatTimestamp (line 41) | async formatTimestamp(turnId: TurnId): Promise<string> {
    method formatAbsoluteTime (line 47) | formatAbsoluteTime(timestamp: number): string {
    method persistTimestamps (line 58) | private async persistTimestamps(): Promise<void> {
    method schedulePersist (line 66) | private schedulePersist(): Promise<void> {
    method flushPersist (line 90) | private async flushPersist(): Promise<void> {
    method clearOldTimestamps (line 101) | async clearOldTimestamps(_conversationId: string): Promise<void> {

FILE: src/pages/content/timestamp/__tests__/TimestampService.test.ts
  class MockStorageService (line 11) | class MockStorageService implements IStorageService {
    method get (line 14) | async get<T>(key: string): Promise<Result<T>> {
    method set (line 22) | async set<T>(key: string, value: T): Promise<Result<void>> {
    method remove (line 27) | async remove(key: string): Promise<Result<void>> {
    method clear (line 32) | async clear(): Promise<Result<void>> {

FILE: src/pages/content/titleUpdater/index.ts
  function startTitleUpdater (line 15) | async function startTitleUpdater() {
  function tryUpdateTitle (line 100) | function tryUpdateTitle() {
  function findChatTitle (line 123) | function findChatTitle(): string | null {

FILE: src/pages/content/visualEffects/rain.ts
  constant CANVAS_ID (line 23) | const CANVAS_ID = 'gv-rain-effect-canvas';
  constant STORAGE_KEY (line 24) | const STORAGE_KEY = 'gvVisualEffect';
  constant LEGACY_KEY (line 25) | const LEGACY_KEY = 'gvSnowEffect';
  constant EFFECT_VALUE (line 26) | const EFFECT_VALUE = 'rain';
  constant WIND_ANGLE (line 29) | const WIND_ANGLE = 0.14;
  constant WIND_DX (line 30) | const WIND_DX = Math.sin(WIND_ANGLE);
  constant WIND_DY (line 31) | const WIND_DY = Math.cos(WIND_ANGLE);
  constant LAYERS (line 33) | const LAYERS = [
  constant MAX_SPLASHES (line 61) | const MAX_SPLASHES = 24;
  type Raindrop (line 63) | interface Raindrop {
  type Splash (line 74) | interface Splash {
  function rand (line 94) | function rand(min: number, max: number): number {
  function createDrop (line 98) | function createDrop(
  function initDrops (line 116) | function initDrops(width: number, height: number): void {
  function spawnSplash (line 129) | function spawnSplash(x: number, y: number): void {
  function updateAndDraw (line 142) | function updateAndDraw(_time: number): void {
  function resizeCanvas (line 234) | function resizeCanvas(): void {
  function startAnimation (line 240) | function startAnimation(): void {
  function stopAnimation (line 245) | function stopAnimation(): void {
  function handleVisibilityChange (line 252) | function handleVisibilityChange(): void {
  function enable (line 260) | function enable(): void {
  function disable (line 296) | function disable(): void {
  function finalizeDrain (line 302) | function finalizeDrain(): void {
  function forceDisable (line 327) | function forceDisable(): void {
  function resolveEffect (line 332) | function resolveEffect(res: Record<string, unknown>): string {
  function startRainEffect (line 338) | function startRainEffect(): void {

FILE: src/pages/content/visualEffects/sakura.ts
  constant CANVAS_ID (line 25) | const CANVAS_ID = 'gv-sakura-effect-canvas';
  constant STORAGE_KEY (line 26) | const STORAGE_KEY = 'gvVisualEffect';
  constant LEGACY_KEY (line 27) | const LEGACY_KEY = 'gvSnowEffect';
  constant EFFECT_VALUE (line 28) | const EFFECT_VALUE = 'sakura';
  constant LAYERS (line 30) | const LAYERS = [
  constant PALETTE (line 43) | const PALETTE = [
  type Petal (line 54) | interface Petal {
  function rand (line 95) | function rand(min: number, max: number): number {
  function createPetal (line 99) | function createPetal(
  function initPetals (line 131) | function initPetals(width: number, height: number): void {
  function tracePetal (line 155) | function tracePetal(c: CanvasRenderingContext2D, s: number): void {
  function updateAndDraw (line 179) | function updateAndDraw(time: number): void {
  function resizeCanvas (line 244) | function resizeCanvas(): void {
  function startAnimation (line 250) | function startAnimation(): void {
  function stopAnimation (line 255) | function stopAnimation(): void {
  function handleVisibilityChange (line 262) | function handleVisibilityChange(): void {
  function enable (line 270) | function enable(): void {
  function disable (line 306) | function disable(): void {
  function finalizeDrain (line 312) | function finalizeDrain(): void {
  function forceDisable (line 336) | function forceDisable(): void {
  function resolveEffect (line 341) | function resolveEffect(res: Record<string, unknown>): string {
  function startSakuraEffect (line 347) | function startSakuraEffect(): void {

FILE: src/pages/content/visualEffects/snow.ts
  constant CANVAS_ID (line 20) | const CANVAS_ID = 'gv-snow-effect-canvas';
  constant STORAGE_KEY (line 21) | const STORAGE_KEY = 'gvVisualEffect';
  constant LEGACY_KEY (line 22) | const LEGACY_KEY = 'gvSnowEffect';
  constant EFFECT_VALUE (line 23) | const EFFECT_VALUE = 'snow';
  constant LAYERS (line 31) | const LAYERS = [
  type Snowflake (line 46) | interface Snowflake {
  function rand (line 68) | function rand(min: number, max: number): number {
  function createSnowflake (line 72) | function createSnowflake(
  function initSnowflakes (line 90) | function initSnowflakes(width: number, height: number): void {
  function updateAndDraw (line 102) | function updateAndDraw(time: number): void {
  function resizeCanvas (line 154) | function resizeCanvas(): void {
  function startAnimation (line 160) | function startAnimation(): void {
  function stopAnimation (line 165) | function stopAnimation(): void {
  function handleVisibilityChange (line 172) | function handleVisibilityChange(): void {
  function enable (line 180) | function enable(): void {
  function disable (line 216) | function disable(): void {
  function finalizeDrain (line 222) | function finalizeDrain(): void {
  function forceDisable (line 246) | function forceDisable(): void {
  function resolveEffect (line 251) | function resolveEffect(res: Record<string, unknown>): string {
  function startSnowEffect (line 261) | function startSnowEffect(): void {

FILE: src/pages/content/watermarkRemover/__tests__/fetchInterceptor.test.ts
  function installInterceptor (line 8) | function installInterceptor(): void {

FILE: src/pages/content/watermarkRemover/alphaMap.ts
  function calculateAlphaMap (line 16) | function calculateAlphaMap(bgCaptureImageData: ImageData): Float32Array {

FILE: src/pages/content/watermarkRemover/blendModes.ts
  constant ALPHA_THRESHOLD (line 12) | const ALPHA_THRESHOLD = 0.002;
  constant MAX_ALPHA (line 13) | const MAX_ALPHA = 0.99;
  constant LOGO_VALUE (line 14) | const LOGO_VALUE = 255;
  type WatermarkPosition (line 16) | interface WatermarkPosition {
  function removeWatermark (line 34) | function removeWatermark(

FILE: src/pages/content/watermarkRemover/credits.ts
  constant WATERMARK_REMOVER_CREDITS (line 43) | const WATERMARK_REMOVER_CREDITS = {

FILE: src/pages/content/watermarkRemover/downloadButton.ts
  constant DOWNLOAD_ICON_SELECTOR (line 1) | const DOWNLOAD_ICON_SELECTOR =
  constant GENERATED_IMAGE_CONTAINER_SELECTOR (line 8) | const GENERATED_IMAGE_CONTAINER_SELECTOR = 'generated-image, .generated-...
  function isWithinGeneratedImageContainer (line 13) | function isWithinGeneratedImageContainer(element: Element): boolean {
  function findNativeDownloadButton (line 17) | function findNativeDownloadButton(target: EventTarget | null): HTMLButto...

FILE: src/pages/content/watermarkRemover/index.ts
  function addDownloadIndicator (line 107) | function addDownloadIndicator(imgElement: HTMLImageElement): void {
  function processImage (line 153) | async function processImage(imgElement: HTMLImageElement): Promise<void> {
  constant GV_BRIDGE_ID (line 221) | const GV_BRIDGE_ID = 'gv-watermark-bridge';
  function getBridgeElement (line 223) | function getBridgeElement(): HTMLElement {
  function notifyFetchInterceptor (line 238) | function notifyFetchInterceptor(enabled: boolean): void {
  function setupFetchInterceptorBridge (line 247) | function setupFetchInterceptorBridge(): void {
  function processImageRequest (line 271) | async function processImageRequest(
  function startWatermarkRemover (line 309) | async function startWatermarkRemover(): Promise<void> {
  constant LARGE_WARNING_AUTO_DISMISS_MS (line 354) | const LARGE_WARNING_AUTO_DISMISS_MS = 8000;
  constant PROCESSING_FALLBACK_AUTO_DISMISS_MS (line 355) | const PROCESSING_FALLBACK_AUTO_DISMISS_MS = 35000;
  type DownloadToastSequence (line 357) | type DownloadToastSequence = {
  function showImmediateDownloadToast (line 379) | function showImmediateDownloadToast(button: HTMLButtonElement): void {
  function setupDownloadButtonTracking (line 420) | function setupDownloadButtonTracking(): void {
  function setupStatusListener (line 437) | function setupStatusListener(): void {

FILE: src/pages/content/watermarkRemover/statusToast.ts
  type StatusToastLevel (line 1) | type StatusToastLevel = 'info' | 'warning' | 'success' | 'error';
  type ToastRecord (line 3) | type ToastRecord = {
  type ToastOptions (line 10) | type ToastOptions = {
  type StatusToastManager (line 16) | type StatusToastManager = {
  type StatusToastManagerOptions (line 34) | type StatusToastManagerOptions = {
  constant STYLE_ID (line 40) | const STYLE_ID = 'gv-status-toast-style';
  constant DEFAULT_CONTAINER_ID (line 41) | const DEFAULT_CONTAINER_ID = 'gv-status-toast-container';
  constant LEVEL_CLASSES (line 42) | const LEVEL_CLASSES: StatusToastLevel[] = ['info', 'warning', 'success',...
  function createStatusToastManager (line 44) | function createStatusToastManager(

FILE: src/pages/content/watermarkRemover/watermarkEngine.ts
  type WatermarkConfig (line 34) | interface WatermarkConfig {
  type WatermarkInfo (line 40) | interface WatermarkInfo {
  function detectWatermarkConfig (line 52) | function detectWatermarkConfig(imageWidth: number, imageHeight: number):...
  function calculateWatermarkPosition (line 78) | function calculateWatermarkPosition(
  type BgCaptures (line 93) | interface BgCaptures {
  class WatermarkEngine (line 102) | class WatermarkEngine {
    method constructor (line 106) | constructor(bgCaptures: BgCaptures) {
    method create (line 111) | static async create(): Promise<WatermarkEngine> {
    method getAlphaMap (line 155) | async getAlphaMap(size: number): Promise<Float32Array> {
    method removeWatermarkFromImage (line 190) | async removeWatermarkFromImage(
    method getWatermarkInfo (line 230) | getWatermarkInfo(imageWidth: number, imageHeight: number): WatermarkIn...

FILE: src/pages/options/Options.tsx
  function OptionsContent (line 7) | function OptionsContent() {
  function Options (line 24) | function Options() {

FILE: src/pages/options/index.tsx
  function init (line 7) | function init() {

FILE: src/pages/panel/Panel.tsx
  function Panel (line 5) | function Panel() {

FILE: src/pages/panel/index.tsx
  function init (line 9) | function init() {

FILE: src/pages/popup/Popup.tsx
  type ScrollMode (line 46) | type ScrollMode = 'jump' | 'flow';
  constant POPUP_SECTION_IDS (line 51) | const POPUP_SECTION_IDS = [
  type PopupSectionId (line 71) | type PopupSectionId = (typeof POPUP_SECTION_IDS)[number];
  constant DEFAULT_SECTION_ORDER (line 73) | const DEFAULT_SECTION_ORDER: readonly PopupSectionId[] = POPUP_SECTION_IDS;
  constant ROOT_CONVERSATIONS_ID (line 75) | const ROOT_CONVERSATIONS_ID = '__root_conversations__';
  function buildFolderPath (line 80) | function buildFolderPath(folderId: string, foldersById: Map<string, Fold...
  function getLanguageName (line 93) | function getLanguageName(lang: string): string {
  function formatFolderStructurePrompt (line 117) | function formatFolderStructurePrompt(
  constant LEGACY_BASELINE_PX (line 234) | const LEGACY_BASELINE_PX = 1200;
  constant FOLDER_SPACING (line 258) | const FOLDER_SPACING = { min: 0, max: 16, defaultValue: 2 };
  constant FOLDER_TREE_INDENT (line 259) | const FOLDER_TREE_INDENT = { min: -8, max: 32, defaultValue: -8 };
  constant CHAT_PERCENT (line 260) | const CHAT_PERCENT = { min: 30, max: 100, defaultValue: 70, legacyBaseli...
  constant EDIT_PERCENT (line 261) | const EDIT_PERCENT = { min: 30, max: 100, defaultValue: 60, legacyBaseli...
  constant SIDEBAR_PERCENT (line 262) | const SIDEBAR_PERCENT = {
  constant SIDEBAR_PX (line 268) | const SIDEBAR_PX = {
  constant AI_STUDIO_SIDEBAR_PX (line 273) | const AI_STUDIO_SIDEBAR_PX = {
  constant LATEST_VERSION_CACHE_KEY (line 290) | const LATEST_VERSION_CACHE_KEY = 'gvLatestVersionCache';
  constant LATEST_VERSION_MAX_AGE (line 291) | const LATEST_VERSION_MAX_AGE = 1000 * 60 * 60 * 6;
  constant SAFARI_DMG_RETRY_AGE (line 292) | const SAFARI_DMG_RETRY_AGE = 1000 * 60 * 30;
  type SettingsUpdate (line 307) | interface SettingsUpdate {
  function SectionReorderControls (line 335) | function SectionReorderControls({
  function Popup (line 404) | function Popup() {

FILE: src/pages/popup/components/CloudSyncSettings.tsx
  function CloudSyncSettings (line 32) | function CloudSyncSettings() {

FILE: src/pages/popup/components/ContextSyncSettings.tsx
  constant STORAGE_KEY_ENABLED (line 8) | const STORAGE_KEY_ENABLED = 'contextSyncEnabled';
  constant STORAGE_KEY_PORT (line 9) | const STORAGE_KEY_PORT = 'contextSyncPort';
  constant DEFAULT_PORT (line 10) | const DEFAULT_PORT = 3030;
  function ContextSyncSettings (line 12) | function ContextSyncSettings() {

FILE: src/pages/popup/components/KeyboardShortcutSettings.tsx
  type RecordingState (line 14) | interface RecordingState {
  function KeyboardShortcutSettings (line 20) | function KeyboardShortcutSettings() {

FILE: src/pages/popup/components/StarredHistory.tsx
  type StarredHistoryProps (line 9) | interface StarredHistoryProps {
  function StarredHistory (line 13) | function StarredHistory({ onClose }: StarredHistoryProps) {

FILE: src/pages/popup/components/WidthSlider.tsx
  type WidthSliderProps (line 7) | interface WidthSliderProps {
  function WidthSlider (line 28) | function WidthSlider({

FILE: src/pages/popup/components/__tests__/CloudSyncSettings.test.tsx
  type MockedChrome (line 23) | type MockedChrome = typeof chrome;
  function createChromeMock (line 31) | function createChromeMock(sendMessage: ReturnType<typeof vi.fn>): Mocked...
  function flushMicrotasks (line 69) | async function flushMicrotasks(): Promise<void> {

FILE: src/pages/popup/index.tsx
  function init (line 10) | function init() {

FILE: src/pages/popup/utils/latestVersion.ts
  type LatestVersionCacheEntry (line 1) | interface LatestVersionCacheEntry {
  function getManifestUpdateUrl (line 9) | function getManifestUpdateUrl(manifest: unknown): string | null {
  function extractLatestReleaseVersion (line 15) | function extractLatestReleaseVersion(data: unknown): string | null {
  function extractDmgDownloadUrl (line 27) | function extractDmgDownloadUrl(data: unknown): string | null {
  function getCachedLatestVersion (line 43) | function getCachedLatestVersion(

FILE: src/tests/setup.ts
  method length (line 42) | get length() {

FILE: src/utils/__tests__/mergeForkNodes.test.ts
  function createForkNode (line 7) | function createForkNode(overrides: Partial<ForkNode> = {}): ForkNode {
  function createForkData (line 20) | function createForkData(

FILE: src/utils/i18n.ts
  type StorageAreaName (line 8) | type StorageAreaName = 'sync' | 'local';
  function getCurrentLanguage (line 54) | async function getCurrentLanguage(): Promise<AppLanguage> {
  function getTranslation (line 73) | async function getTranslation(key: TranslationKey): Promise<string> {
  function getTranslationSync (line 84) | function getTranslationSync(key: TranslationKey): string {
  function getTranslationSyncUnsafe (line 89) | function getTranslationSyncUnsafe(key: string): string {
  function initI18n (line 98) | async function initI18n(): Promise<void> {
  function setCachedLanguage (line 116) | function setCachedLanguage(lang: AppLanguage): void {
  function createTranslator (line 124) | function createTran
Condensed preview — 648 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,005K chars).
[
  {
    "path": ".claude/rules/content-scripts.md",
    "chars": 1221,
    "preview": "---\nglobs: [\"src/pages/content/**\", \"public/contentStyle.css\"]\n---\n\n# Content Script Rules\n\n## CSS\n- All injected CSS cl"
  },
  {
    "path": ".claude/rules/high-complexity.md",
    "chars": 1260,
    "preview": "---\nglobs: [\"src/core/services/StorageService.ts\", \"src/core/services/DataBackupService.ts\", \"src/core/services/GoogleDr"
  },
  {
    "path": ".claude/rules/i18n.md",
    "chars": 605,
    "preview": "---\nglobs: [\"src/locales/**\"]\n---\n\n# i18n / Translation Rules\n\n## 10 Locales (ALL must be updated together)\n`en`, `ar`, "
  },
  {
    "path": ".claude/rules/typescript.md",
    "chars": 1173,
    "preview": "---\nglobs: [\"src/**/*.ts\", \"src/**/*.tsx\"]\n---\n\n# TypeScript Coding Standards\n\n## DOs\n- Prefer plain objects with interf"
  },
  {
    "path": ".claude/settings.json",
    "chars": 1573,
    "preview": "{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Task\",\n        \"hooks\": [\n          {\n            \"type\""
  },
  {
    "path": ".claude/skills/safari-release/SKILL.md",
    "chars": 1910,
    "preview": "---\nname: safari-release\ndescription: Build Safari extension with update check enabled, guide user through Xcode export,"
  },
  {
    "path": ".editorconfig",
    "chars": 188,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\ntrim_"
  },
  {
    "path": ".entire/.gitignore",
    "chars": 41,
    "preview": "tmp/\nsettings.local.json\nmetadata/\nlogs/\n"
  },
  {
    "path": ".entire/settings.json",
    "chars": 75,
    "preview": "{\n  \"strategy\": \"manual-commit\",\n  \"enabled\": true,\n  \"telemetry\": false\n}\n"
  },
  {
    "path": ".gitattributes",
    "chars": 459,
    "preview": "# Auto detect text files and normalize line endings to LF\n* text=auto eol=lf\n\n# Explicitly declare file types\n*.ts text "
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 13664,
    "preview": "> **🌐 语言 / Language**: [中文](#贡献指南) | [English](#contributing-to-gemini-voyager) | [Español](CONTRIBUTING_ES.md) | [Franç"
  },
  {
    "path": ".github/CONTRIBUTING_ES.md",
    "chars": 9144,
    "preview": "# Guía de Contribución\n\n> [!CAUTION]\n> **Este proyecto actualmente NO acepta PRs para nuevas funcionalidades.** Si tiene"
  },
  {
    "path": ".github/CONTRIBUTING_FR.md",
    "chars": 9600,
    "preview": "# Guide de Contribution\n\n> [!CAUTION]\n> **Ce projet n'accepte actuellement PAS les PRs pour de nouvelles fonctionnalités"
  },
  {
    "path": ".github/CONTRIBUTING_JA.md",
    "chars": 5894,
    "preview": "# 貢献ガイド\n\n> [!CAUTION]\n> **本プロジェクトは現在、新機能の PR を受け付けていません。** どうしても実装したい機能がある場合は、以下のプロセスに従ってください:\n>\n> 1. **まず Issue を作成して**"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 84,
    "preview": "github: Nagi-ovo\nbuy_me_a_coffee: Nag1ovo\ncustom: ['https://afdian.com/a/nagi-ovo']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 3290,
    "preview": "name: '🐛 反馈缺陷 Bug Report'\ndescription: '反馈一个问题缺陷 | Report a bug'\ntitle: '[Bug] '\nlabels: '🐛 Bug'\nbody:\n  - type: markdow"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 225,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: '💬 讨论区 Discussions'\n    url: https://github.com/Nagi-ovo/gemini-voy"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "chars": 2267,
    "preview": "name: '🌠 功能需求 Feature Request'\ndescription: '需求或建议 | Suggest an idea for Gemini Voyager'\ntitle: '[Feature] '\nlabels: '🌠 "
  },
  {
    "path": ".github/README_AR.md",
    "chars": 14279,
    "preview": "<div align=\"center\" dir=\"rtl\">\n  <img src=\"../docs/public/assets/promotion/Promo-Banner.png\" alt=\"promotion\"/>\n  <h3>اجع"
  },
  {
    "path": ".github/README_ES.md",
    "chars": 15563,
    "preview": "<div align=\"center\">\n  <img src=\"../docs/public/assets/promotion/Promo-Banner.png\" alt=\"promotion\"/>\n  <h3>Haz que tu ex"
  },
  {
    "path": ".github/README_FR.md",
    "chars": 15481,
    "preview": "<div align=\"center\">\n  <img src=\"../docs/public/assets/promotion/Promo-Banner.png\" alt=\"promotion\"/>\n  <h3>Personnalisez"
  },
  {
    "path": ".github/README_JA.md",
    "chars": 12745,
    "preview": "<div align=\"center\">\n  <img src=\"../docs/public/assets/promotion/Promo-Banner-jp.png\" alt=\"promotion\"/>\n  <h3>Gemini™ 体験"
  },
  {
    "path": ".github/README_KO.md",
    "chars": 14224,
    "preview": "<div align=\"center\">\n  <img src=\"../docs/public/assets/promotion/Promo-Banner-KO.png\" alt=\"promotion\"/>\n  <h3>Gemini™ 경험"
  },
  {
    "path": ".github/README_PT.md",
    "chars": 15629,
    "preview": "<div align=\"center\">\n  <img src=\"../docs/public/assets/promotion/Promo-Banner.png\" alt=\"promotion\"/>\n  <h3>Torne a sua e"
  },
  {
    "path": ".github/README_RU.md",
    "chars": 15179,
    "preview": "<div align=\"center\">\n  <img src=\"../docs/public/assets/promotion/Promo-Banner.png\" alt=\"promotion\"/>\n  <h3>Сделайте ваш "
  },
  {
    "path": ".github/README_ZH.md",
    "chars": 12794,
    "preview": "<div align=\"center\">\n  <img src=\"../docs/public/assets/promotion/Promo-Banner-cn.png\" alt=\"promotion\"/>\n  <h3>打造属于你的 Gem"
  },
  {
    "path": ".github/README_ZH_TW.md",
    "chars": 13004,
    "preview": "<div align=\"center\">\n  <img src=\"../docs/public/assets/promotion/Promo-Banner-cn.png\" alt=\"promotion\"/>\n  <h3>打造屬於你的 Gem"
  },
  {
    "path": ".github/RELEASE_TEMPLATE.md",
    "chars": 612,
    "preview": "## ✨ What's New\n\n<!-- Add main features here -->\n\n---\n\n## 🐛 Bug Fixes\n\n<!-- Add bug fixes here -->\n\n---\n\n## 📚 Documentat"
  },
  {
    "path": ".github/docs/CHANGELOG.md",
    "chars": 2617,
    "preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
  },
  {
    "path": ".github/docs/IMPORT_EXPORT_GUIDE.md",
    "chars": 4863,
    "preview": "# Folder Import/Export Guide\n\n## Overview\n\nThe folder configuration import/export feature allows you to sync folder conf"
  },
  {
    "path": ".github/docs/IMPORT_EXPORT_GUIDE_ZH.md",
    "chars": 2759,
    "preview": "# 文件夹导出/导入功能使用指南\n\n## 功能概述\n\n文件夹配置导出/导入功能允许您在不同设备间同步文件夹配置,无需搭建服务器。\n\n## 使用方法\n\n### 📥 导出文件夹配置(下载 ⬇️)\n\n1. 打开 Gemini 聊天页面\n2. 在文"
  },
  {
    "path": ".github/docs/safari/INSTALLATION.md",
    "chars": 1237,
    "preview": "# Safari Extension Installation Guide\n\nEnglish | [简体中文](INSTALLATION_ZH.md)\n\nA simple guide for installing Voyager on Sa"
  },
  {
    "path": ".github/docs/safari/INSTALLATION_ZH.md",
    "chars": 787,
    "preview": "# Safari 扩展安装指南\n\n[English](INSTALLATION.md) | 简体中文\n\n在 Safari 上安装 Voyager 的简单指南。\n\n## 系统要求\n\n- **macOS 11+**\n- **Safari 14+"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 1762,
    "preview": "### 🚫 AI Policy / AI 政策\n\n- **We explicitly reject AI-generated PRs that have not been manually verified.**\n- **本项目拒绝接受任何"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 815,
    "preview": "name: CI\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  build-and-check:\n    runs-on: u"
  },
  {
    "path": ".github/workflows/deploy-docs.yml",
    "chars": 1105,
    "preview": "name: Deploy Docs\n\non:\n  push:\n    branches:\n      - main\n    paths:\n      - 'docs/**'\n      - '.vitepress/**'\n      - '"
  },
  {
    "path": ".github/workflows/issue-claim.yml",
    "chars": 4985,
    "preview": "name: Issue Claim\n\non:\n  issue_comment:\n    types: [created]\n\njobs:\n  claim-issue:\n    runs-on: ubuntu-latest\n    if: gi"
  },
  {
    "path": ".github/workflows/issue-validator.yml",
    "chars": 3733,
    "preview": "name: Issue Validator\n\non:\n  issues:\n    types: [opened, edited]\n\njobs:\n  validate:\n    runs-on: ubuntu-latest\n    permi"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 10072,
    "preview": "name: Release\nrun-name: >\n  ${{\n    (github.event_name == 'workflow_dispatch' && inputs.version && format('chore(release"
  },
  {
    "path": ".github/workflows/sponsors.yml",
    "chars": 821,
    "preview": "name: Update Sponsors\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - main\n    paths:\n      - 'sponsorkit/sponso"
  },
  {
    "path": ".github/workflows/stale.yml",
    "chars": 813,
    "preview": "name: 'Close Stale Issues'\non:\n  schedule:\n    - cron: '30 1 * * *'\n  workflow_dispatch:\n\npermissions:\n  issues: write\n "
  },
  {
    "path": ".gitignore",
    "chars": 873,
    "preview": "node_modules/\n/dist_chrome/\n/dist_firefox/\n/dist_safari/\ngemini-voyager-edge-*.zip\n\n# Vitest\ncoverage/\n.vitest/\n\n# Gener"
  },
  {
    "path": ".prettierignore",
    "chars": 613,
    "preview": "# Sync project\ngemini-voyager-sync/\n\n# Build outputs\ndist_chrome/\ndist_firefox/\ndist_safari/\ndist/\n\n# Dependencies\nnode_"
  },
  {
    "path": ".prettierrc",
    "chars": 535,
    "preview": "{\n  \"semi\": true,\n  \"singleQuote\": true,\n  \"tabWidth\": 2,\n  \"useTabs\": false,\n  \"printWidth\": 100,\n  \"trailingComma\": \"a"
  },
  {
    "path": "CLAUDE.md",
    "chars": 4130,
    "preview": "# CLAUDE.md - Gemini Voyager\n\n## Commands\n\n```bash\nbun install                # Setup\nbun run dev:chrome         # Dev ("
  },
  {
    "path": "CNAME",
    "chars": 16,
    "preview": "voyager.nagi.fun"
  },
  {
    "path": "LICENSE",
    "chars": 35214,
    "preview": "Copyright (c) 2022 Jonathan Braat\nCopyright (c) 2026 Jesse Zhang\n\n                    GNU GENERAL PUBLIC LICENSE\n       "
  },
  {
    "path": "README.md",
    "chars": 16776,
    "preview": "<div align=\"center\">\n  <img src=\"docs/public/assets/promotion/Promo-Banner.png\" alt=\"promotion\"/>\n  <h3>Make Your Gemini"
  },
  {
    "path": "commitlint.config.cjs",
    "chars": 617,
    "preview": "module.exports = {\n  extends: ['@commitlint/config-conventional'],\n  rules: {\n    'type-enum': [\n      2,\n      'always'"
  },
  {
    "path": "custom-vite-plugins.ts",
    "chars": 3951,
    "preview": "import fs from 'fs';\nimport { resolve } from 'path';\nimport type { NormalizedInputOptions, NormalizedOutputOptions } fro"
  },
  {
    "path": "docs/.vitepress/config.mts",
    "chars": 29296,
    "preview": "import {\n  GitChangelog,\n  GitChangelogMarkdownSection,\n} from '@nolebase/vitepress-plugin-git-changelog/vite';\nimport {"
  },
  {
    "path": "docs/.vitepress/theme/components/HomeAskAI.vue",
    "chars": 7968,
    "preview": "<script setup lang=\"ts\">\nimport { useData } from 'vitepress';\nimport { computed } from 'vue';\n\nconst { lang } = useData("
  },
  {
    "path": "docs/.vitepress/theme/components/HomeReviews.vue",
    "chars": 10441,
    "preview": "<script setup lang=\"ts\">\nimport { useData } from 'vitepress';\nimport { computed } from 'vue';\nimport { Vue3Marquee } fro"
  },
  {
    "path": "docs/.vitepress/theme/components/HomeTeaser.vue",
    "chars": 1457,
    "preview": "<script setup lang=\"ts\">\nimport { useData } from 'vitepress';\n\nconst { frontmatter } = useData();\n</script>\n\n<template>\n"
  },
  {
    "path": "docs/.vitepress/theme/components/README.md",
    "chars": 1047,
    "preview": "# SafariDownloadLink Component\n\nThis Vue component automatically fetches the latest release version from GitHub and gene"
  },
  {
    "path": "docs/.vitepress/theme/components/SafariDownloadLink.vue",
    "chars": 2051,
    "preview": "<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue';\n\nconst latestVersion = ref('1.2.4'); // Default"
  },
  {
    "path": "docs/.vitepress/theme/index.ts",
    "chars": 1040,
    "preview": "import { NolebaseGitChangelogPlugin } from '@nolebase/vitepress-plugin-git-changelog/client';\nimport '@nolebase/vitepres"
  },
  {
    "path": "docs/.vitepress/theme/style.css",
    "chars": 1048,
    "preview": "/**\n * Customize default theme styling by overriding CSS variables:\n * https://github.com/vuejs/vitepress/blob/main/src/"
  },
  {
    "path": "docs/ar/guide/batch-delete.md",
    "chars": 1213,
    "preview": "# الحذف الجماعي\n\nاحذف محادثات متعددة في وقت واحد، وداعاً للحذف واحدة تلو الأخرى.\n\n## المميزات\n\n- **وضع التحديد المتعدد**"
  },
  {
    "path": "docs/ar/guide/cloud-sync.md",
    "chars": 2021,
    "preview": "# مزامنة السحابية\n\nقم بمزامنة مجلداتك ومكتبة المطالبات والبيانات الأخرى مع Google Drive للحفاظ على اتساق تجربتك عبر الأج"
  },
  {
    "path": "docs/ar/guide/community.md",
    "chars": 1590,
    "preview": "# المجتمع والملاحظات\n\nنحن نقدر صوت كل مستخدم. سواء واجهت خطأً (Bug)، أو كان لديك اقتراح لميزة، أو كنت ترغب في مشاركة مكت"
  },
  {
    "path": "docs/ar/guide/context-sync.md",
    "chars": 2704,
    "preview": "# نقل الذاكرة: مزامنة السياق (تجريبي)\n\n**أبعاد مختلفة، مشاركة سلسة**\n\nقم بتطوير المنطق على الويب وتنفيذ الكود في بيئة ال"
  },
  {
    "path": "docs/ar/guide/deep-research.md",
    "chars": 2021,
    "preview": "# تصدير Deep Research\n\nقم بتصدير التقرير النهائي الذي أنشأته Deep Research، أو احفظ عملية \"التفكير\" الكاملة كملف Markdow"
  },
  {
    "path": "docs/ar/guide/default-model.md",
    "chars": 1163,
    "preview": "# النموذج الافتراضي\n\n::: info\n**ملاحظة**: هذه الميزة مدعومة في الإصدار 1.1.9 وما بعده.\n:::\n\nقم بتعيين نموذج Gemini™ المف"
  },
  {
    "path": "docs/ar/guide/export.md",
    "chars": 1718,
    "preview": "# Total Freedom\n\nData lock-in is the enemy.\nWe believe that if you create it, you own it.\n\n## Export Everything\n\nVoyager"
  },
  {
    "path": "docs/ar/guide/folders.md",
    "chars": 4242,
    "preview": "# المجلدات كما يجب أن تكون\n\nلماذا تنظيم محادثات الذكاء الاصطناعي صعب للغاية؟\nلقد أصلحنا ذلك. قمنا ببناء نظام ملفات لأفكا"
  },
  {
    "path": "docs/ar/guide/fork.md",
    "chars": 833,
    "preview": "# تفريع المحادثة (تجريبي)\n\nيجب ألا يكون التفكير طريقاً ذا اتجاه واحد. في الاستكشافات المعقدة، غالباً ما نحتاج إلى العودة"
  },
  {
    "path": "docs/ar/guide/formula-copy.md",
    "chars": 1498,
    "preview": "# نسخ الصيغ\n\nيجعل Voyager من السهل جداً إعادة استخدام الصيغ الرياضية والرموز العلمية. وهو يدعم النسخ بنقرة واحدة لكود مص"
  },
  {
    "path": "docs/ar/guide/getting-started.md",
    "chars": 1408,
    "preview": "# مرحباً بك على متن الرحلة\n\nتهانينا. لقد قمت للتو بترقية عقلك.\nVoyager ليس مجرد أداة مساعدة؛ إنه سير عمل. إليك كيفية الا"
  },
  {
    "path": "docs/ar/guide/input-collapse.md",
    "chars": 664,
    "preview": "# طي الإدخال\n\nقم بطي منطقة الإدخال عندما تكون فارغة للحصول على مساحة قراءة أكبر. انقر فوق الشريط المطوي للتوسيع والبدء ف"
  },
  {
    "path": "docs/ar/guide/installation.md",
    "chars": 3057,
    "preview": "# التثبيت\n\n::: info أخبار\n🍎 **إضافة Safari الأصلية تم إطلاقها!** هي مجانية بالكامل وتدعم التثبيت بنقرة واحدة.\n:::\n\nاختر "
  },
  {
    "path": "docs/ar/guide/markdown-fix.md",
    "chars": 434,
    "preview": "# إصلاح عرض Markdown\n\nتقوم واجهة ويب Gemini™ أحياناً بإدراج عناصر HTML (مثل مصادر الاقتباس أو علامات التمييز) داخل النص،"
  },
  {
    "path": "docs/ar/guide/mermaid.md",
    "chars": 1635,
    "preview": "# Mermaid Diagram Rendering\n\nAutomatically render Mermaid code as visual diagrams.\n\n## Overview\n\nWhen Gemini™ outputs Me"
  },
  {
    "path": "docs/ar/guide/nanobanana.md",
    "chars": 2294,
    "preview": "# NanoBanana Option\n\n::: warning توافق المتصفح\nحالياً، ميزة **NanoBanana** **غير مدعومة على متصفح Safari** بسبب قيود واج"
  },
  {
    "path": "docs/ar/guide/prevent-auto-scroll.md",
    "chars": 722,
    "preview": "# منع التمرير التلقائي\n\nعندما تقوم بقراءة المحادثات السابقة، فإن إرسال مطالبة (Prompt) جديدة وتوليد إجابة من Gemini™ يجب"
  },
  {
    "path": "docs/ar/guide/prompts.md",
    "chars": 1298,
    "preview": "# أصلك الرقمي: مكتبة المطالبات\n\nقضيت وقتاً طويلاً في كتابة مطالبة (Prompt) مذهلة ساعدت كثيراً.\nتستخدمها وترميها؟\nلا، احف"
  },
  {
    "path": "docs/ar/guide/quote-reply.md",
    "chars": 460,
    "preview": "# الرد مع اقتباس\n\nحدد للاقتباس، تماماً مثل Discord أو Slack.\n\nحدد أي نص في رد Gemini™، وسيظهر زر **\"اقتباس\"**. انقر عليه"
  },
  {
    "path": "docs/ar/guide/recents-hider.md",
    "chars": 1590,
    "preview": "# إخفاء العناصر الأخيرة والـ Gems\n\n::: info\n**ملاحظة**: هذه الميزة مدعومة في الإصدار 1.1.9 وما بعده.\n:::\n\nأضف مفتاح تبدي"
  },
  {
    "path": "docs/ar/guide/settings.md",
    "chars": 1129,
    "preview": "# عرض الدردشة\n\nاستخدم شاشتك العريضة بالكامل.\n\nيقوم Gemini™ بتقييد المحتوى الرئيسي بعرض ثابت. يقوم Voyager بتحرير ذلك.\nان"
  },
  {
    "path": "docs/ar/guide/sidebar-auto-hide.md",
    "chars": 680,
    "preview": "# إخفاء الشريط الجانبي تلقائياً\n\nهل تريد تجربة دردشة أكثر اندماجاً؟\n\nنحن نوفر ميزة **إخفاء الشريط الجانبي تلقائياً**. عن"
  },
  {
    "path": "docs/ar/guide/sidebar.md",
    "chars": 728,
    "preview": "# عرض الشريط الجانبي\n\nهل أسماء المجلدات طويلة جدًا ولا تظهر بالكامل؟\nأو هل يأخذ الشريط الجانبي مساحة كبيرة من الدردشة؟\n\n"
  },
  {
    "path": "docs/ar/guide/sponsor.md",
    "chars": 1377,
    "preview": "# رعاية المشروع\n\n> [!NOTE]\n> إذا كان Voyager مفيداً لك، فشاركْه على X أو Reddit أو YouTube إلخ. كل مشاركة تساعد المزيد م"
  },
  {
    "path": "docs/ar/guide/tab-title.md",
    "chars": 425,
    "preview": "# مزامنة عنوان علامة التبويب\n\nلا تضيع أبداً في بحر من علامات تبويب \"Gemini™\".\n\nافتراضياً، تقول جميع علامات تبويب Gemini "
  },
  {
    "path": "docs/ar/guide/timeline.md",
    "chars": 873,
    "preview": "# تصفح الجدول الزمني\n\nميزة Voyager المميزة. طريقة بصرية ومكانية لتصفح محادثاتك.\n\n## المشكلة\n\nيمكن أن تصبح محادثات الذكاء"
  },
  {
    "path": "docs/ar/index.md",
    "chars": 6812,
    "preview": "---\nlayout: home\n\nhero:\n  name: 'Voyager'\n  text: 'نظام التشغيل المفقود لـ Gemini.'\n  tagline: 'نحن نحب Gemini. أردنا فق"
  },
  {
    "path": "docs/ar/privacy.md",
    "chars": 2455,
    "preview": "# سياسة الخصوصية\n\nآخر تحديث: 16 مارس 2026\n\n## مقدمة\n\nيلتزم Voyager (\"نحن\"، \"نا\"، أو \"لنا\") بحماية خصوصيتك. توضح سياسة ال"
  },
  {
    "path": "docs/en/guide/batch-delete.md",
    "chars": 1341,
    "preview": "# Batch Delete\n\nDelete multiple conversations at once, no more deleting one by one.\n\n## Features\n\n- **Multi-select Mode*"
  },
  {
    "path": "docs/en/guide/cloud-sync.md",
    "chars": 2113,
    "preview": "# Cloud Sync\n\nSync your folders, prompt library, and other data to Google Drive to keep your experience consistent acros"
  },
  {
    "path": "docs/en/guide/community.md",
    "chars": 1637,
    "preview": "# Community & Feedback\n\nWe value every user's voice. Whether you've found a bug, have a feature suggestion, or want to s"
  },
  {
    "path": "docs/en/guide/context-sync.md",
    "chars": 2966,
    "preview": "# Memory Transport: Context Sync (Experimental)\n\n**Different Dimensions, Seamless Sharing**\n\nIterate logic on the web, a"
  },
  {
    "path": "docs/en/guide/deep-research.md",
    "chars": 2160,
    "preview": "# Deep Research Export\n\nExport the final report generated by Deep Research, or save its complete \"thinking\" process as a"
  },
  {
    "path": "docs/en/guide/default-model.md",
    "chars": 1195,
    "preview": "# Default Model\n\n::: info\n**Note**: This feature is supported in version 1.1.9 and later.\n:::\n\nSet a preferred Gemini™ m"
  },
  {
    "path": "docs/en/guide/export.md",
    "chars": 1733,
    "preview": "# Total Freedom\n\nData lock-in is the enemy.\nWe believe that if you create it, you own it.\n\n## Export Everything\n\nVoyager"
  },
  {
    "path": "docs/en/guide/folders.md",
    "chars": 4486,
    "preview": "# Folders Done Right\n\nWhy is organizing AI chats so hard?\nWe fixed it. We built a file system for your thoughts.\n\n<div s"
  },
  {
    "path": "docs/en/guide/fork.md",
    "chars": 1019,
    "preview": "# Conversation Fork (Experimental)\n\nThinking shouldn't be a one-way street. In complex explorations, we often need to re"
  },
  {
    "path": "docs/en/guide/formula-copy.md",
    "chars": 1624,
    "preview": "# Formula Copy\n\nVoyager makes it effortless to reuse mathematical formulas and scientific symbols. It supports one-click"
  },
  {
    "path": "docs/en/guide/getting-started.md",
    "chars": 1528,
    "preview": "# Welcome Aboard\n\nCongratulations. You’ve just upgraded your intellect.\nVoyager isn’t just a utility; it’s a workflow. H"
  },
  {
    "path": "docs/en/guide/input-collapse.md",
    "chars": 712,
    "preview": "# Input Collapse\n\nCollapse the input area when empty to gain more reading space. Click the collapsed bar to expand and s"
  },
  {
    "path": "docs/en/guide/installation.md",
    "chars": 3273,
    "preview": "# Installation\n\n::: info News\n🍎 **Safari Native Extension is launched!** It is completely free and supports one-click in"
  },
  {
    "path": "docs/en/guide/markdown-fix.md",
    "chars": 515,
    "preview": "# Markdown Rendering Fix\n\nThe Gemini™ web interface sometimes inserts HTML elements (such as citation sources or highlig"
  },
  {
    "path": "docs/en/guide/mermaid.md",
    "chars": 1676,
    "preview": "# Mermaid Diagram Rendering\n\nAutomatically render Mermaid code as visual diagrams.\n\n## Overview\n\nWhen Gemini™ outputs Me"
  },
  {
    "path": "docs/en/guide/nanobanana.md",
    "chars": 2327,
    "preview": "# NanoBanana Option\n\n::: warning Browser Compatibility\nCurrently, the **NanoBanana** feature is **not supported on Safar"
  },
  {
    "path": "docs/en/guide/prevent-auto-scroll.md",
    "chars": 731,
    "preview": "# Prevent Auto Scroll\n\nWhen you are reading past conversations, hitting Enter to send a new prompt causes Gemini™ to for"
  },
  {
    "path": "docs/en/guide/prompts.md",
    "chars": 1435,
    "preview": "# Your Intellectual Assets: Prompt Library\n\nYou craft a perfect prompt. It solves a complex coding problem or writes a b"
  },
  {
    "path": "docs/en/guide/quote-reply.md",
    "chars": 1560,
    "preview": "# Quote Reply\n\nVoyager offers a convenient \"Quote Reply\" feature, making replies to specific content more precise and ef"
  },
  {
    "path": "docs/en/guide/recents-hider.md",
    "chars": 1673,
    "preview": "# Hide Recent Items and Gems\n\n::: info\n**Note**: This feature is supported in version 1.1.9 and later.\n:::\n\nAdd an elega"
  },
  {
    "path": "docs/en/guide/settings.md",
    "chars": 1830,
    "preview": "# Make It Yours\n\nThe default experience is great. But you might want it perfect.\nCustomize every pixel.\n\n## Cinema Mode\n"
  },
  {
    "path": "docs/en/guide/sidebar-auto-hide.md",
    "chars": 714,
    "preview": "# Sidebar Auto-hide\n\nWant a more immersive chat experience?\n\nWe provide a **Sidebar Auto-hide** feature. When enabled, t"
  },
  {
    "path": "docs/en/guide/sidebar.md",
    "chars": 883,
    "preview": "# Sidebar Width\n\nFolder names too long to see?\nOr is the sidebar taking up too much precious chat space?\n\nNow you can fr"
  },
  {
    "path": "docs/en/guide/sponsor.md",
    "chars": 3429,
    "preview": "# Sponsor\n\n> [!NOTE]\n> If Voyager helps you, feel free to share it on X, Reddit, YouTube, Threads, etc. Every share help"
  },
  {
    "path": "docs/en/guide/tab-title.md",
    "chars": 1115,
    "preview": "# Tab Title Sync\n\nAutomatically syncs the browser tab title with the current Gemini™ chat title.\n\n## Features\n\n- **Real-"
  },
  {
    "path": "docs/en/guide/timeline.md",
    "chars": 1248,
    "preview": "# Time Travel\n\nLong conversations are messy. You scroll up, you scroll down, you lose your place.\nVoyager turns your con"
  },
  {
    "path": "docs/en/index.md",
    "chars": 7436,
    "preview": "---\nlayout: home\n\nhero:\n  name: 'Voyager'\n  text: 'The missing OS for Gemini.'\n  tagline: 'We love Gemini. We just wante"
  },
  {
    "path": "docs/en/privacy.md",
    "chars": 2720,
    "preview": "# Privacy Policy\n\nLast updated: March 16, 2026\n\n## Introduction\n\nVoyager (\"we\", \"our\", or \"us\") is committed to protecti"
  },
  {
    "path": "docs/es/guide/batch-delete.md",
    "chars": 1672,
    "preview": "# Eliminación por Lote\n\nElimina múltiples conversaciones a la vez, di adiós a la tediosa eliminación una por una.\n\n## Ca"
  },
  {
    "path": "docs/es/guide/cloud-sync.md",
    "chars": 2515,
    "preview": "# Sincronización en la Nube\n\nSincroniza tus carpetas, biblioteca de prompts y otros datos en Google Drive para mantener "
  },
  {
    "path": "docs/es/guide/community.md",
    "chars": 1787,
    "preview": "# Comunidad y Comentarios\n\nValoramos mucho la voz de cada usuario. Ya sea que encuentres un error, tengas una sugerencia"
  },
  {
    "path": "docs/es/guide/context-sync.md",
    "chars": 3354,
    "preview": "# Transporte de memoria: Sincronización de contexto (Experimental)\n\n**Diferentes dimensiones, intercambio fluido**\n\nDesa"
  },
  {
    "path": "docs/es/guide/deep-research.md",
    "chars": 2615,
    "preview": "# Exportación de Deep Research\n\nExporta el informe final generado por Deep Research o guarda su proceso de \"pensamiento\""
  },
  {
    "path": "docs/es/guide/default-model.md",
    "chars": 1408,
    "preview": "# Modelo Predeterminado\n\n::: info\n**Nota**: Esta función es compatible con la versión 1.1.9 y posteriores.\n:::\n\nEstablec"
  },
  {
    "path": "docs/es/guide/export.md",
    "chars": 1897,
    "preview": "# Libertad Total\n\nLos datos bloqueados son la peor experiencia.\nNuestro credo es simple: lo que creas, es tuyo.\n\n## Llév"
  },
  {
    "path": "docs/es/guide/folders.md",
    "chars": 5061,
    "preview": "# Carpetas bien hechas\n\n¿Por qué es tan difícil organizar los chats de IA?\nLo hemos solucionado. Hemos construido un sis"
  },
  {
    "path": "docs/es/guide/fork.md",
    "chars": 1155,
    "preview": "# Bifurcación de Conversación (Experimental)\n\nEl pensamiento no debe ser un camino de un solo sentido. En exploraciones "
  },
  {
    "path": "docs/es/guide/formula-copy.md",
    "chars": 1878,
    "preview": "# Copia de Fórmulas\n\nVoyager facilita enormemente la reutilización de fórmulas matemáticas y símbolos científicos. Admit"
  },
  {
    "path": "docs/es/guide/getting-started.md",
    "chars": 1521,
    "preview": "# Bienvenido a Bordo\n\nFelicidades. Tu flujo de trabajo acaba de subir de categoría.\nVoyager no es solo una herramienta, "
  },
  {
    "path": "docs/es/guide/input-collapse.md",
    "chars": 858,
    "preview": "# Colapso del Cuadro de Entrada\n\nColapsa automáticamente el cuadro de entrada cuando está vacío para ganar más espacio d"
  },
  {
    "path": "docs/es/guide/installation.md",
    "chars": 3558,
    "preview": "# Instalación\n\n::: info Noticias\n🍎 **¡La extensión nativa de Safari ya está disponible!** Es completamente gratuita y se"
  },
  {
    "path": "docs/es/guide/markdown-fix.md",
    "chars": 592,
    "preview": "# Corrección de Renderizado Markdown\n\nLa interfaz web de Gemini™ a veces inserta elementos HTML (como fuentes de citas o"
  },
  {
    "path": "docs/es/guide/mermaid.md",
    "chars": 2067,
    "preview": "# Renderizado de Gráficos Mermaid\n\nRenderiza automáticamente código Mermaid en gráficos visuales.\n\n## Introducción\n\nCuan"
  },
  {
    "path": "docs/es/guide/nanobanana.md",
    "chars": 2927,
    "preview": "# Opción NanoBanana\n\n::: warning Compatibilidad de navegadores\nActualmente, la función **NanoBanana** **no es compatible"
  },
  {
    "path": "docs/es/guide/prevent-auto-scroll.md",
    "chars": 902,
    "preview": "# Evitar desplazamiento automático\n\nCuando estás leyendo conversaciones anteriores, si envías un nuevo mensaje, Gemini™ "
  },
  {
    "path": "docs/es/guide/prompts.md",
    "chars": 1578,
    "preview": "# Tus Activos Digitales: Biblioteca de Prompts\n\nPasaste mucho tiempo escribiendo un Prompt de nivel divino, fue de gran "
  },
  {
    "path": "docs/es/guide/quote-reply.md",
    "chars": 1798,
    "preview": "# Respuesta con Cita\n\nVoyager ofrece una conveniente función de \"Respuesta con Cita\", haciendo que responder a contenido"
  },
  {
    "path": "docs/es/guide/recents-hider.md",
    "chars": 1950,
    "preview": "# Ocultar elementos recientes y Gems\n\n::: info\n**Nota**: Esta función es compatible con la versión 1.1.9 y posteriores.\n"
  },
  {
    "path": "docs/es/guide/settings.md",
    "chars": 2051,
    "preview": "# A Tu Gusto\n\nLo predeterminado ya es bueno. Pero queremos la perfección.\nTu territorio, tus reglas.\n\n## Modo Cine\n\n¿Por"
  },
  {
    "path": "docs/es/guide/sidebar-auto-hide.md",
    "chars": 818,
    "preview": "# Ocultar barra lateral automáticamente\n\n¿Quieres una experiencia de chat más inmersiva?\n\nOfrecemos la función de **Ocul"
  },
  {
    "path": "docs/es/guide/sidebar.md",
    "chars": 918,
    "preview": "# Ancho de la barra lateral\n\n¿Los nombres de las carpetas son demasiado largos?\n¿O la barra lateral ocupa demasiado espa"
  },
  {
    "path": "docs/es/guide/sponsor.md",
    "chars": 3566,
    "preview": "# Patrocinar\n\n> [!NOTE]\n> Si Voyager te resulta útil, compártelo en X, Reddit, YouTube, etc. Cada difusión ayuda a que m"
  },
  {
    "path": "docs/es/guide/tab-title.md",
    "chars": 1491,
    "preview": "# Sincronización de Título de Pestaña\n\nSincroniza automáticamente el título de la pestaña del navegador con el título de"
  },
  {
    "path": "docs/es/guide/timeline.md",
    "chars": 1344,
    "preview": "# Viaje en el Tiempo\n\nLas conversaciones largas son un desastre. Arriba y abajo, perdido.\nVoyager convierte la conversac"
  },
  {
    "path": "docs/es/index.md",
    "chars": 8060,
    "preview": "---\nlayout: home\n\nhero:\n  name: 'Voyager'\n  text: 'Finalmente, está completo.'\n  tagline: 'Pensamiento tangible, todo en"
  },
  {
    "path": "docs/es/privacy.md",
    "chars": 3276,
    "preview": "# Política de Privacidad\n\nÚltima actualización: 16 de marzo de 2026\n\n## Introducción\n\nVoyager (en adelante \"nosotros\") s"
  },
  {
    "path": "docs/fr/guide/batch-delete.md",
    "chars": 1708,
    "preview": "# Suppression par Lot\n\nSupprimez plusieurs conversations à la fois, fini la suppression une par une.\n\n## Fonctionnalités"
  },
  {
    "path": "docs/fr/guide/cloud-sync.md",
    "chars": 2617,
    "preview": "# Synchronisation Cloud\n\nSynchronisez vos dossiers, votre bibliothèque de prompts et d'autres données sur Google Drive p"
  },
  {
    "path": "docs/fr/guide/community.md",
    "chars": 1899,
    "preview": "# Communauté & Feedback\n\nNous apprécions la voix de chaque utilisateur. Que vous ayez trouvé un bug, une suggestion de f"
  },
  {
    "path": "docs/fr/guide/context-sync.md",
    "chars": 3398,
    "preview": "# Transport de mémoire : Synchronisation du contexte (Expérimental)\n\n**Différentes dimensions, partage fluide**\n\nÉlabore"
  },
  {
    "path": "docs/fr/guide/deep-research.md",
    "chars": 2546,
    "preview": "# Export Deep Research\n\nExportez le rapport final généré par Deep Research ou enregistrez son processus de \"réflexion\" c"
  },
  {
    "path": "docs/fr/guide/default-model.md",
    "chars": 1426,
    "preview": "# Modèle par Défaut\n\n::: info\n**Note** : Cette fonctionnalité est prise en charge dans la version 1.1.9 et ultérieure.\n:"
  },
  {
    "path": "docs/fr/guide/export.md",
    "chars": 2030,
    "preview": "# Liberté Totale\n\nLe verrouillage des données est l'ennemi.\nNous croyons que si vous le créez, vous le possédez.\n\n## Tou"
  },
  {
    "path": "docs/fr/guide/folders.md",
    "chars": 5257,
    "preview": "# Les dossiers, comme ils devraient l'être\n\nPourquoi organiser les chats AI est-il si difficile ?\nNous avons réglé ça. N"
  },
  {
    "path": "docs/fr/guide/fork.md",
    "chars": 1218,
    "preview": "# Bifurcation de Conversation (Expérimental)\n\nLa pensée ne devrait pas être à sens unique. Dans les explorations complex"
  },
  {
    "path": "docs/fr/guide/formula-copy.md",
    "chars": 1966,
    "preview": "# Copie de Formules\n\nVoyager facilite grandement la réutilisation de formules mathématiques et de symboles scientifiques"
  },
  {
    "path": "docs/fr/guide/getting-started.md",
    "chars": 1617,
    "preview": "# Bienvenue à Bord\n\nFélicitations. Vous venez de mettre à niveau votre intellect.\nVoyager n'est pas juste un utilitaire "
  },
  {
    "path": "docs/fr/guide/input-collapse.md",
    "chars": 829,
    "preview": "# Réduction de l'Entrée\n\nRéduisez la zone de saisie lorsqu'elle est vide pour gagner plus d'espace de lecture. Cliquez s"
  },
  {
    "path": "docs/fr/guide/installation.md",
    "chars": 3825,
    "preview": "# Installation\n\n::: info Nouvelles\n🍎 **L'extension native Safari est disponible !** Elle est entièrement gratuite et s'i"
  },
  {
    "path": "docs/fr/guide/markdown-fix.md",
    "chars": 617,
    "preview": "# Correction du Rendu Markdown\n\nL'interface web de Gemini™ insère parfois des éléments HTML (tels que des sources de cit"
  },
  {
    "path": "docs/fr/guide/mermaid.md",
    "chars": 1980,
    "preview": "# Rendu de Diagrammes Mermaid\n\nRendez automatiquement le code Mermaid sous forme de diagrammes visuels.\n\n## Aperçu\n\nLors"
  },
  {
    "path": "docs/fr/guide/nanobanana.md",
    "chars": 2689,
    "preview": "# Option NanoBanana\n\n::: warning Compatibilité du navigateur\nActuellement, la fonction **NanoBanana** n'est **pas prise "
  },
  {
    "path": "docs/fr/guide/prevent-auto-scroll.md",
    "chars": 880,
    "preview": "# Empêcher le défilement auto\n\nLorsque vous lisez vos conversations passées, si vous appuyez sur Entrée pour envoyer un "
  },
  {
    "path": "docs/fr/guide/prompts.md",
    "chars": 1742,
    "preview": "# Vos Actifs Intellectuels : Bibliothèque de Prompts\n\nVous rédigez un prompt parfait. Il résout un problème de code comp"
  },
  {
    "path": "docs/fr/guide/quote-reply.md",
    "chars": 1889,
    "preview": "# Réponse avec Citation\n\nVoyager offre une fonctionnalité pratique de \"Réponse avec Citation\", rendant les réponses à un"
  },
  {
    "path": "docs/fr/guide/recents-hider.md",
    "chars": 1964,
    "preview": "# Masquer les éléments récents et les Gems\n\n::: info\n**Note**: Cette fonctionnalité est supportée dans la version 1.1.9 "
  },
  {
    "path": "docs/fr/guide/settings.md",
    "chars": 2074,
    "preview": "# Appropriez-le-vous\n\nL'expérience par défaut est géniale. Mais vous pourriez la vouloir parfaite.\nPersonnalisez chaque "
  },
  {
    "path": "docs/fr/guide/sidebar-auto-hide.md",
    "chars": 856,
    "preview": "# Masquer automatiquement la barre latérale\n\nVous voulez une expérience de chat plus immersive ?\n\nNous proposons une fon"
  },
  {
    "path": "docs/fr/guide/sidebar.md",
    "chars": 949,
    "preview": "# Largeur de la barre latérale\n\nLes noms de dossiers sont trop longs ?\nOu la barre latérale prend-elle trop de place ?\n\n"
  },
  {
    "path": "docs/fr/guide/sponsor.md",
    "chars": 3521,
    "preview": "# Sponsor\n\n> [!NOTE]\n> Si Voyager vous est utile, partagez-le sur X, Reddit, YouTube, etc. Chaque partage aide plus de p"
  },
  {
    "path": "docs/fr/guide/tab-title.md",
    "chars": 1390,
    "preview": "# Synchro du Titre d'Onglet\n\nSynchronise automatiquement le titre de l'onglet du navigateur avec le titre du chat Gemini"
  },
  {
    "path": "docs/fr/guide/timeline.md",
    "chars": 1560,
    "preview": "# Voyage dans le Temps\n\nLes longues conversations sont désordonnées. Vous faites défiler vers le haut, vers le bas, vous"
  },
  {
    "path": "docs/fr/index.md",
    "chars": 7921,
    "preview": "---\nlayout: home\n\nhero:\n  name: 'Voyager'\n  text: \"L'OS manquant pour Gemini.\"\n  tagline: \"Nous aimons Gemini. Nous voul"
  },
  {
    "path": "docs/fr/privacy.md",
    "chars": 3466,
    "preview": "# Politique de Confidentialité\n\nDernière mise à jour : 16 mars 2026\n\n## Introduction\n\nVoyager (\"nous\", \"notre\", ou \"nos\""
  },
  {
    "path": "docs/guide/batch-delete.md",
    "chars": 587,
    "preview": "# 批量删除\n\n一次性删除多个对话,告别逐个删除的繁琐操作。\n\n## 功能介绍\n\n- **多选模式**:长按任意对话进入多选模式,可勾选多个要删除的对话。\n- **一键清理**:选中后点击删除按钮,批量删除所有选中的对话。\n- **进度反馈"
  },
  {
    "path": "docs/guide/cloud-sync.md",
    "chars": 1152,
    "preview": "# 云同步\n\n将您的文件夹、灵感库(Prompts)等数据同步到 Google Drive,在不同设备间保持一致。\n\n## 功能特点\n\n- **多端同步**:利用 Google Drive 云端存储,在多台电脑上同步您的配置。\n- **全面"
  },
  {
    "path": "docs/guide/community.md",
    "chars": 1154,
    "preview": "# 交流与反馈\n\n我们非常重视每一位用户的声音。无论你是遇到了 Bug、有功能建议,还是想分享你构建的指令宝库,都可以通过以下方式与我们联系。\n\n## 📢 关注动态\n\n关注我们的 X (Twitter) 账号,获取最新开发进展。\n\n- **"
  },
  {
    "path": "docs/guide/context-sync.md",
    "chars": 1518,
    "preview": "# 记忆搬运:上下文同步(实验性)\n\n**不同次元,丝滑共享**\n\n在网页端推演逻辑,在 IDE 里落地代码。 Voyager 打通次元壁,让你的 IDE 瞬间拥有网页端的“思维过程”。\n\n## 告别反复横跳\n\n开发者最烦的事:在网页上聊透"
  },
  {
    "path": "docs/guide/deep-research.md",
    "chars": 1216,
    "preview": "# Deep Research 导出\n\n导出 Deep Research 生成的最终报告,或将其完整的“思考”过程保存为 Markdown 文件。\n\n## 1. 报告导出 (PDF / 图片)\n\nDeep Research 生成的报告支持导"
  },
  {
    "path": "docs/guide/default-model.md",
    "chars": 664,
    "preview": "# 默认模型\n\n::: info\n**注意**:该功能仅在 1.1.9 及后续版本中支持。\n:::\n\n为 Gemini™ 添加设置默认模型的功能,避免每次开启新对话时都需要手动切换。\n\n<div style=\"text-align: cen"
  },
  {
    "path": "docs/guide/export.md",
    "chars": 1150,
    "preview": "# 彻底自由\n\n数据被锁死,是最坏的体验。\n我们信条很简单:你创造的,就是你的。\n\n## 带走一切\n\nVoyager 帮你把数据从云端拽回手心。\n\n### 格式随你选\n\n- **Markdown**:给 Obsidian 或 Notion "
  },
  {
    "path": "docs/guide/folders.md",
    "chars": 2709,
    "preview": "# 文件夹,本该如此\n\n整理 AI 聊天记录,以前怎么那么难?\n我们修好了。给你的思绪,装个文件系统。\n\n<div style=\"display: flex; gap: 20px; margin-top: 20px; flex-wrap: "
  },
  {
    "path": "docs/guide/fork.md",
    "chars": 361,
    "preview": "# 对话分支 (实验性)\n\n思维不应是一条单行道。在复杂的探索中,我们常常需要回到某个关键节点,尝试不同的可能性。\n\nVoyager 带来的 **对话分支** 功能,让你能够轻松发散思维,探索对话的平行宇宙。\n\n## 功能介绍\n\n> **⚠"
  },
  {
    "path": "docs/guide/formula-copy.md",
    "chars": 776,
    "preview": "# 公式复制\n\nVoyager 让数学公式和科学符号的复用变得异常简单。支持一键复制 LaTeX 源码以及兼容 Microsoft Word 的 MathML 格式。\n\n## 功能介绍\n\n当你要求 Gemini 推导公式或编写数学表达式时,"
  },
  {
    "path": "docs/guide/getting-started.md",
    "chars": 565,
    "preview": "# 欢迎登船\n\n恭喜。你的工作流刚刚升舱了。\nVoyager 不只是工具,它是种习惯。给我 5 分钟,带你上手。\n\n## 1. 就位\n\n还没装?去 [安装指南](/guide/installation)。\n装好了?刷新 Gemini 页面。"
  },
  {
    "path": "docs/guide/input-collapse.md",
    "chars": 394,
    "preview": "# 输入框折叠\n\n输入框为空时自动折叠,获得更多阅读空间。点击折叠后的按钮即可展开输入。\n\n<div style=\"text-align: center; margin-top: 20px;\">\n  <img src=\"/assets/hi"
  },
  {
    "path": "docs/guide/installation.md",
    "chars": 2134,
    "preview": "# 安装\n\n::: info 新闻\n🍎 **Safari 浏览器原生插件已推出!** 现在支持一键安装并完全免费。\n:::\n\n选一条路。\n\n> ⚠️ 提示词管理器是唯一支持 Gemini™ 企业版的功能。\n\n## 1. 官方商店(推荐)\n\n"
  },
  {
    "path": "docs/guide/markdown-fix.md",
    "chars": 191,
    "preview": "# Markdown 渲染修复\n\nGemini™ 的网页界面有时会在文本中插入 HTML 元素(例如引用来源或高亮标记),这可能会破坏 Markdown 的加粗语法(`**text**`),导致文本无法正确加粗显示。\n\nVoyager 内置"
  },
  {
    "path": "docs/guide/mermaid.md",
    "chars": 861,
    "preview": "# Mermaid 图表渲染\n\n自动将 Mermaid 代码渲染为可视化图表。\n\n## 功能介绍\n\n当 Gemini™ 输出 Mermaid 代码块时(如流程图、时序图、甘特图等),Voyager 会自动检测并渲染为交互式图表。\n\n### "
  },
  {
    "path": "docs/guide/nanobanana.md",
    "chars": 1322,
    "preview": "# NanoBanana 选项\n\n::: warning 浏览器兼容性\n目前 **NanoBanana** 去水印功能由于浏览器 API 限制,**暂不支持 Safari 浏览器**。如果您需要使用此功能,建议使用 **Chrome** 或"
  },
  {
    "path": "docs/guide/prevent-auto-scroll.md",
    "chars": 300,
    "preview": "# 防自动跳转\n\n在查看过往对话时,如果您输入了新问题并按下回车,Gemini™ 默认会将页面强制滚动到最底部以显示最新生成的回答。这可能会打断您的阅读体验。\n\n**防自动跳转** 功能可以拦截这种不必要的滚动行为:\n\n- 当您向上滚动查看"
  },
  {
    "path": "docs/guide/prompts.md",
    "chars": 674,
    "preview": "# 你的数字资产:提示词库\n\n磨了半天写出个神级 Prompt,帮大忙了。\n用完就丢?\n不,存起来。\n\n## 指令宝库\n\n这是你的指令宝库。\n\n### 1. 捕获\n\n写出好东西了?点输入框旁边的 **浮窗图标**。\n存入库中,落袋为安。\n\n"
  },
  {
    "path": "docs/guide/quote-reply.md",
    "chars": 609,
    "preview": "# 引用回复\n\nVoyager 提供了便捷的“引用回复”功能,让针对特定内容的回复更加精准高效。\n\n## 功能介绍\n\n在日常对话中,我们经常需要针对 AI 输出的某一段具体内容进行追问或反驳。传统的做法是复制那段话,然后在输入框里手打 `>"
  },
  {
    "path": "docs/guide/recents-hider.md",
    "chars": 1014,
    "preview": "# 隐藏最近项目和 Gem\n\n::: info\n**注意**:该功能仅在 1.1.9 及后续版本中支持。\n:::\n\n为 Gemini™ 首页的“最近保存”部分添加一个优雅的切换开关,让界面更加简洁。现在也支持隐藏侧边栏的 **Gems** "
  },
  {
    "path": "docs/guide/settings.md",
    "chars": 1001,
    "preview": "# 随心所欲\n\n默认的已经很好。但我们要的是完美。\n你的地盘,你做主。\n\n## 影院模式\n\n为什么要透过门缝看世界?\n把聊天框拉大。\n\n- **宽屏**:1400px。写代码、看大表,视野全开。\n- **专注**:800px。沉浸阅读,心无"
  },
  {
    "path": "docs/guide/sidebar-auto-hide.md",
    "chars": 441,
    "preview": "# 侧边栏自动收起\n\n想要更加沉浸式的对话体验?\n\n我们提供了**自动收起侧边栏**的功能。开启后,当你把鼠标移出侧边栏区域时,它会自动收起;当你把鼠标移入侧边栏区域时,它会自动展开。\n\n### 演示\n\n<div style=\"text-a"
  },
  {
    "path": "docs/guide/sidebar.md",
    "chars": 463,
    "preview": "# 侧边栏宽度\n\n文件夹名字太长,显示不全?\n或者觉得侧边栏太宽,占用了宝贵的聊天空间?\n\n现在,你可以自由调整侧边栏的宽度。\n\n## 如何调整\n\n1. 打开 Voyager 的设置面板(点击浏览器右上角的插件图标)。\n   <img sr"
  },
  {
    "path": "docs/guide/sponsor.md",
    "chars": 2761,
    "preview": "# 赞助\n\n> [!NOTE]\n> 如果 Voyager 帮到了你,欢迎分享到 X、即刻、小红书、Linux.do、V2EX 等等,也欢迎推荐给海外 KOL。每一次分享都能让更多人看到这个项目,从而改善 Gemini 的使用体验。谢谢。\n\n"
  },
  {
    "path": "docs/guide/tab-title.md",
    "chars": 590,
    "preview": "# 标签页标题同步\n\n自动将浏览器标签页标题同步为当前 Gemini™ 对话的标题。\n\n## 功能介绍\n\n- **实时同步**:当对话标题发生变化时(例如 AI 生成了新标题或你手动重命名了对话),浏览器标签页标题不仅仅是 \"Gemini\""
  },
  {
    "path": "docs/guide/timeline.md",
    "chars": 495,
    "preview": "# 时间旅行\n\n长对话是灾难。上上下下,找不着北。\nVoyager 把对话变成一条线。\n\n## 看见节奏\n\n看屏幕右侧。\n每个点都是一句话。那是你对话的脉搏。\n\n## 导航,一步到位\n\n- **瞬移**:点哪去哪,绝不拖泥带水。\n- **偷"
  },
  {
    "path": "docs/index.md",
    "chars": 5884,
    "preview": "---\nlayout: home\n\nhero:\n  name: 'Voyager'\n  text: '终于,它完整了。'\n  tagline: '思维有形,万物归位。'\n  image:\n    src: /logo.png\n    alt"
  },
  {
    "path": "docs/ja/guide/batch-delete.md",
    "chars": 747,
    "preview": "# 一括削除\n\n複数の会話を一度に削除し、一つずつ消す面倒な作業に別れを告げましょう。\n\n## 機能紹介\n\n- **複数選択モード**:任意の会話を長押しすると複数選択モードに入り、削除したい会話を複数チェックできます。\n- **一括削除*"
  },
  {
    "path": "docs/ja/guide/cloud-sync.md",
    "chars": 1451,
    "preview": "# クラウド同期\n\nフォルダ、プロンプトライブラリ、その他のデータを Google ドライブに同期して、デバイス間で一貫した体験を維持します。\n\n## 機能の特徴\n\n- **マルチデバイス同期**: Google ドライブを使用して、複数の"
  },
  {
    "path": "docs/ja/guide/community.md",
    "chars": 1274,
    "preview": "# コミュニティ\n\n私たちは、すべてのユーザーの声を大切にしています。バグを見つけたとき、機能のアイデアがあるとき、あるいは自慢のプロンプト集を共有したいときなど、以下の方法でいつでもご連絡ください。\n\n## 📢 最新情報\n\nX (Twit"
  }
]

// ... and 448 more files (download for full content)

About this extraction

This page contains the full source code of the Nagi-ovo/gemini-voyager GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 648 files (3.6 MB), approximately 982.6k tokens, and a symbol index with 2132 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!