Skip to content

feat(plugins): add ChatGPT conversation export - #921

Open
TanChuping wants to merge 7 commits into
Nagi-ovo:mainfrom
TanChuping:feat/chatgpt-export-plugin
Open

feat(plugins): add ChatGPT conversation export#921
TanChuping wants to merge 7 commits into
Nagi-ovo:mainfrom
TanChuping:feat/chatgpt-export-plugin

Conversation

@TanChuping

@TanChuping TanChuping commented Aug 9, 2026

Copy link
Copy Markdown

Description

  • Add an opt-in native plugin for exporting ChatGPT conversations as Markdown, JSON, or PDF.
  • Support whole-conversation export, progressive inline message selection without forced full-history loading, and temporary-chat handoff.
  • Keep ChatGPT access behind optional host permission and manage all injected UI through PluginScope lifecycle cleanup.
  • Add a compact ChatGPT-style print layout with rich-content and captured-text fallbacks.

Related Issue

Refs #841

PR #920 opened while this branch was being prepared. It was not open at the preceding check, so the two implementations arrived independently within minutes of each other.

Visual Proof

Format selection dialog:

ChatGPT export format selection

PDF output:

ChatGPT-style PDF output

Browser Testing

Tested commit: a5c6960

Browser / version Scenario and result Evidence
Chrome 151.0.7922.76 Enabled/disabled the plugin; exercised whole and selected Markdown/JSON export, PDF print preview, temporary-chat handoff, and lifecycle cleanup. Passed. See Visual Proof

Missing checks and owner, or N/A reason:

  • Edge, Firefox, and Safari live browser workflows were not exercised. Their production builds passed; follow-up owner: @TanChuping.

Commands not run and reason:

  • None.

Verification notes:

  • bun run format: passed.
  • bun run lint: passed with 0 errors; existing repository warnings remain.
  • bun run verify:pr: ran through the standard checks. 2667/2668 tests passed; the only failure is an unrelated Windows path-separator assertion for allowed embedded provisioning profiles.
  • Latest review-fix targeted Vitest run: 28/28 passed.
  • Chrome, Firefox, Safari, and Edge production builds passed. Edge packaging required a local Windows zip compatibility shim; no shim or build output is included in this PR.
  • bun run docs:build: passed.
  • KaTeX PDF export verification: passed.

Checklist

  • If I used an agent, I discussed the requirement, affected scope, and verification plan clearly.
  • I have manually verified that the feature works as intended.
  • For UI/behavior changes, I have tried the real workflow for about 15 minutes when possible.
  • For UI/behavior changes, I have included visual proof after verification.
  • I have confirmed that this PR does not break existing functionality.
  • This PR focuses on one issue or one coherent change.
  • I ran bun run format, bun run lint, then the standard local bun run verify:pr, and documented the local verification exceptions above.
  • I added/updated regression tests for behavior changes.
  • I listed the affected browsers actually tested and identified the follow-up owner.

Add opt-in Markdown, JSON, PDF, selection, and temporary-chat export flows.

Co-authored-by: Codex <codex@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds a ChatGPT export plugin for Markdown, JSON, and PDF. It collects virtualized conversations, supports message selection and temporary-chat handoff, adds localized UI, and introduces ChatGPT-specific PDF rendering.

Changes

ChatGPT export

Layer / File(s) Summary
Conversation collection and metadata
src/features/plugins/builtin/chatgptExport/conversation.ts, src/features/plugins/builtin/chatgptExport/conversation.test.ts
Collects messages across supported DOM schemas and virtualized scroll windows. Builds ordered turns and conversation metadata.
Export payloads and extraction
src/features/plugins/builtin/chatgptExport/exporter.ts, src/features/export/services/DOMContentExtractor.ts, src/features/plugins/builtin/chatgptExport/exporter.test.ts, src/features/export/services/__tests__/DOMContentExtractor.test.ts
Generates Markdown and JSON payloads, downloads files, delegates PDF export, and preserves links, attachments, and code blocks.
ChatGPT PDF rendering
src/features/export/services/PDFPrintService.ts, src/features/export/services/__tests__/PDFPrintService.test.ts
Adds ChatGPT appearance options, title handling, fallback content, metadata propagation, and ChatGPT-specific print CSS.
Plugin activation and export UI
src/features/plugins/builtin/chatgptExport/index.ts, src/features/plugins/builtin/chatgptExport/ui.ts, src/features/plugins/builtin/chatgptExport/styles.ts, src/features/plugins/builtin/chatgptExport/i18n.ts, src/features/plugins/builtin/index.ts, src/pages/content/pluginNativeRegistration.ts
Adds export-button injection, menus, dialogs, progress, toasts, lifecycle cleanup, localization, responsive styling, and native registration.
Message selection
src/features/plugins/builtin/chatgptExport/selectionMode.ts, src/features/plugins/builtin/chatgptExport/selectionMode.test.ts
Adds inline message selection controls that preserve selections across virtualization, scrolling, and DOM mutations.
Temporary-chat handoff
src/features/plugins/builtin/chatgptExport/tempHandoff.ts, src/features/plugins/builtin/chatgptExport/tempHandoff.test.ts
Adds inline or attachment-based transcript handoff, session persistence, temporary-chat exit, composer insertion, and post-navigation resumption.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ChatGPT
  participant ChatGptExportPlugin
  participant conversation.ts
  participant exporter.ts
  participant PDFPrintService
  ChatGPT->>ChatGptExportPlugin: Open export menu
  ChatGptExportPlugin->>conversation.ts: Collect conversation messages
  conversation.ts-->>ChatGptExportPlugin: Return snapshots and metadata
  ChatGptExportPlugin->>exporter.ts: Submit export format
  exporter.ts->>PDFPrintService: Export PDF with ChatGPT appearance
  PDFPrintService-->>ChatGptExportPlugin: Return generated filename
Loading

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely identifies the main change: adding ChatGPT conversation export support.
Description check ✅ Passed The description covers the feature scope, related issue, visual proof, browser testing, verification results, exceptions, and checklist.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (1)
src/features/export/services/__tests__/PDFPrintService.test.ts (1)

95-124: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the title assertion discriminating, and reduce CSS string coupling.

Two points:

  1. Line 96 and Line 105 use the same value, Export test. The ChatGPT branch at getPrintDialogTitle prefers metadata.title over the page title. With identical values the assertion at Line 116 passes for either precedence order. Use distinct values to lock the intended behavior.
  2. Lines 117-123 assert exact CSS declaration text, including spacing. A reformat of buildChatGptPrintStyles breaks these tests without any behavior change. Assert on the scoped selector plus a small number of load-bearing declarations, or assert on computed markup instead.
💚 Proposed change for point 1
-    document.title = 'Export test';
+    document.title = 'Stale page title';
     window.print = vi.fn();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/export/services/__tests__/PDFPrintService.test.ts` around lines
95 - 124, Update the ChatGPT layout test around PDFPrintService.export to use
distinct page and metadata titles, then assert the resulting document.title
reflects the metadata-title precedence. Replace exact CSS string/spacing
assertions with less brittle checks that verify the scoped selector and only
essential load-bearing declarations or computed markup behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/features/export/services/PDFPrintService.ts`:
- Around line 534-543: Update isMeaningfulConversationTitle to reject the
literal title “ChatGPT” alongside the existing Gemini exclusions, so
getConversationTitle cannot use document.title as a meaningful fallback and
empty metadata titles resolve to the intended neutral fallback.
- Around line 1262-1283: Update the generated code-block styles in the PDF print
template around the `.gv-print-turn-text pre` rule to preserve readable contrast
when print background graphics are disabled. Keep the existing dark styling by
default, and add a `forced-colors` or no-background safeguard that switches the
code surface and text to a light, bordered presentation without relying on the
dark background.

In `@src/features/plugins/builtin/chatgptExport/conversation.ts`:
- Around line 256-275: Update the collection loop around the conversation
collector to merge mounted messages after the final scroll, including when
maxSteps is reached. If collection remains above the bottom once the step limit
is exhausted, return an explicit incomplete-collection error instead of
exporting collected messages; preserve normal completion behavior at the bottom.
Add a regression test using a small maxSteps value that verifies the error and
final-scroll merge.
- Around line 118-132: Update readStableId so fallback identities never use the
scan-local order, which is unstable across virtualized windows. Prefer a stable
ChatGPT identifier for every supported schema; when none exists, return a
non-deduplicating identity or otherwise ensure the message is retained rather
than merged in collected. Add coverage for window swaps containing repeated
role/text messages.

In `@src/features/plugins/builtin/chatgptExport/exporter.test.ts`:
- Around line 18-89: Add a JSON export test to the ChatGPT export formats suite
using exportChatGptConversation with format 'json'; stub the Blob/download path,
parse the generated payload, and assert the schema preserves selected state,
message IDs, roles, and content. Reuse collectMountedChatGptMessages and the
existing metadata setup, while keeping the current Markdown and PDF coverage
unchanged.

In `@src/features/plugins/builtin/chatgptExport/i18n.ts`:
- Line 93: Update the tempRegret and tempTitle translation strings in the chat
export i18n definitions to describe saving and transferring the temporary chat
to a normal chat, replacing the regret wording. Keep both labels aligned and
readable across themes.

In `@src/features/plugins/builtin/chatgptExport/index.ts`:
- Around line 67-70: The whole-conversation icon path in the paths mapping
should use coordinates that keep the downward arrow within the SVG viewport.
Update the whole entry’s arrow segment while preserving its existing vertical
stem and bottom bar; leave the selected and temporary paths unchanged.

In `@src/features/plugins/builtin/chatgptExport/styles.ts`:
- Around line 284-315: Complete the dark-theme selector coverage in the styles
for .gv-chatgpt-export-menu-item:hover,
.gv-chatgpt-export-menu-item:focus-visible, .gv-chatgpt-export-btn:hover,
.gv-chatgpt-export-format-option:has(input:checked), and
.gv-chatgpt-export-button--floating by adding the missing body.dark variants.
Preserve the existing light-theme rules and dark colors for html.dark,
body.dark, and [data-theme='dark'].

In `@src/features/plugins/builtin/chatgptExport/tempHandoff.test.ts`:
- Around line 19-53: Extend the temporary chat handoff tests to cover
handoffTemporaryChat and resumePendingHandoff lifecycle behavior, pending-entry
expiry, and attachment-delivery failure. Verify failed attachment delivery
preserves the existing composer draft instead of clearing it through
input.replaceChildren(), while retaining the current route, transcript, and
attachment assertions. Run bun run test, bun run lint, and bun run typecheck.

In `@src/features/plugins/builtin/chatgptExport/tempHandoff.ts`:
- Around line 182-187: Update the attachment fallback in tempHandoff around
dispatchPaste so a false result never calls input.replaceChildren or overwrites
the existing composer draft. Instead, return a delivery failure and display the
appropriate error while preserving the user's current content; keep the
successful paste path unchanged.
- Around line 106-110: Update writePending and resumePendingHandoff to persist a
stable active ChatGPT account identity in PendingHandoff and compare it before
delivery; clear the pending handoff and skip delivery when identities differ.
Also adjust the attachment-paste fallback in deliver so it preserves existing
composer content or requires confirmation before replacing it, rather than
clearing the draft automatically.

In `@src/features/plugins/builtin/chatgptExport/ui.ts`:
- Around line 45-83: Update the dialog creation and lifecycle around the
existing overlay/dialog helpers to add an accessible name via the title element,
then reuse the established gv-pm-* modal precedent for Tab focus trapping,
Escape/outside dismissal, focus restoration to the trigger, teardown, and theme
behavior. Add keyboard coverage verifying Tab cycling within the modal and
restoring focus after close.

---

Nitpick comments:
In `@src/features/export/services/__tests__/PDFPrintService.test.ts`:
- Around line 95-124: Update the ChatGPT layout test around
PDFPrintService.export to use distinct page and metadata titles, then assert the
resulting document.title reflects the metadata-title precedence. Replace exact
CSS string/spacing assertions with less brittle checks that verify the scoped
selector and only essential load-bearing declarations or computed markup
behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b7469713-a40e-4214-a43b-f3ca24e62781

📥 Commits

Reviewing files that changed from the base of the PR and between 7800540 and 2bb4b8e.

📒 Files selected for processing (19)
  • src/features/export/services/PDFPrintService.ts
  • src/features/export/services/__tests__/PDFPrintService.test.ts
  • src/features/plugins/builtin/builtin.test.ts
  • src/features/plugins/builtin/chatgptExport/conversation.test.ts
  • src/features/plugins/builtin/chatgptExport/conversation.ts
  • src/features/plugins/builtin/chatgptExport/exporter.test.ts
  • src/features/plugins/builtin/chatgptExport/exporter.ts
  • src/features/plugins/builtin/chatgptExport/i18n.ts
  • src/features/plugins/builtin/chatgptExport/index.test.ts
  • src/features/plugins/builtin/chatgptExport/index.ts
  • src/features/plugins/builtin/chatgptExport/selectionMode.test.ts
  • src/features/plugins/builtin/chatgptExport/selectionMode.ts
  • src/features/plugins/builtin/chatgptExport/styles.ts
  • src/features/plugins/builtin/chatgptExport/tempHandoff.test.ts
  • src/features/plugins/builtin/chatgptExport/tempHandoff.ts
  • src/features/plugins/builtin/chatgptExport/ui.test.ts
  • src/features/plugins/builtin/chatgptExport/ui.ts
  • src/features/plugins/builtin/index.ts
  • src/pages/content/pluginNativeRegistration.ts

Comment thread src/features/export/services/PDFPrintService.ts
Comment thread src/features/export/services/PDFPrintService.ts
Comment thread src/features/plugins/builtin/chatgptExport/conversation.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/conversation.ts
Comment thread src/features/plugins/builtin/chatgptExport/exporter.test.ts
Comment thread src/features/plugins/builtin/chatgptExport/styles.ts
Comment thread src/features/plugins/builtin/chatgptExport/tempHandoff.test.ts
Comment thread src/features/plugins/builtin/chatgptExport/tempHandoff.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/tempHandoff.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/ui.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2bb4b8e4ef

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/features/plugins/builtin/chatgptExport/conversation.ts
Comment thread src/features/plugins/builtin/chatgptExport/exporter.ts
Comment on lines +132 to +133
export function getChatGptExportCopy(): ChatGptExportCopy {
return /^zh(?:-|_|$)/i.test(navigator.language) ? ZH : EN;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Localize the export UI for all supported locales

This chooses copy from navigator.language rather than Voyager's stored language and only provides English and Chinese dictionaries; the manifest similarly supplies only zh and zh_TW. Users selecting ar, es, fr, ja, ko, pt, or ru therefore see English plugin metadata and controls, and even a Chinese Voyager setting is ignored when the browser language differs. Load the configured language and provide copy for the complete supported locale set.

AGENTS.md reference: AGENTS.md:L82-L82

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The preference bug is fixed in 1f0e159: runtime copy follows the stored Voyager language. This plugin intentionally ships authored English and Chinese copy with English fallback; I am not adding unreviewed machine translations for the other locales in this fix.

Co-authored-by: Codex <codex@users.noreply.github.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 655ea91a99

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/features/plugins/builtin/chatgptExport/conversation.ts
Comment thread src/features/plugins/builtin/chatgptExport/tempHandoff.ts
Co-authored-by: Codex <codex@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/features/plugins/builtin/chatgptExport/tempHandoff.test.ts`:
- Around line 122-139: Add a test for the immediate account-change path in
handoffTemporaryChat, covering navigation from /u/0/ to /u/1/. Assert it returns
account-mismatch, preserves the existing composer draft, and removes the pending
state; then run bun run test, bun run lint, and bun run typecheck.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b363876c-3abf-4109-ab80-962d65fdc8de

📥 Commits

Reviewing files that changed from the base of the PR and between 2bb4b8e and 1f0e159.

📒 Files selected for processing (14)
  • src/features/export/services/PDFPrintService.ts
  • src/features/export/services/__tests__/PDFPrintService.test.ts
  • src/features/plugins/builtin/chatgptExport/conversation.test.ts
  • src/features/plugins/builtin/chatgptExport/conversation.ts
  • src/features/plugins/builtin/chatgptExport/exporter.test.ts
  • src/features/plugins/builtin/chatgptExport/exporter.ts
  • src/features/plugins/builtin/chatgptExport/i18n.ts
  • src/features/plugins/builtin/chatgptExport/index.test.ts
  • src/features/plugins/builtin/chatgptExport/index.ts
  • src/features/plugins/builtin/chatgptExport/styles.ts
  • src/features/plugins/builtin/chatgptExport/tempHandoff.test.ts
  • src/features/plugins/builtin/chatgptExport/tempHandoff.ts
  • src/features/plugins/builtin/chatgptExport/ui.test.ts
  • src/features/plugins/builtin/chatgptExport/ui.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/features/plugins/builtin/chatgptExport/i18n.ts
  • src/features/plugins/builtin/chatgptExport/index.test.ts
  • src/features/export/services/tests/PDFPrintService.test.ts
  • src/features/plugins/builtin/chatgptExport/ui.test.ts
  • src/features/plugins/builtin/chatgptExport/ui.ts
  • src/features/plugins/builtin/chatgptExport/conversation.ts
  • src/features/export/services/PDFPrintService.ts

Comment thread src/features/plugins/builtin/chatgptExport/tempHandoff.test.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1f0e159199

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/features/plugins/builtin/chatgptExport/tempHandoff.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/conversation.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/tempHandoff.ts Outdated
Co-authored-by: Codex <codex@users.noreply.github.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 096a5f8c69

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/features/plugins/builtin/chatgptExport/tempHandoff.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/conversation.ts Outdated
Comment thread src/features/export/services/DOMContentExtractor.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/features/export/services/DOMContentExtractor.ts`:
- Around line 82-87: Update extractAssistantContent and its processNodes flow to
include direct text nodes alongside child elements, preserving their DOM order
when combining ordinary user-content such as text surrounding links. Ensure
mixed content retains both surrounding text and extracted element output, and
add a regression case covering text before and after an anchor.
- Around line 82-87: Update the empty-content fallback in DOMContentExtractor so
standard ChatGPT file-pill metadata is extracted and merged into
ExtractedContent.attachments instead of relying only on
extractAssistantContent(element). Preserve the existing text and HTML fallback
behavior, and add a regression test covering standard file pills in the JSON
export.
- Around line 610-624: Update both anchor-handling branches, including the
standalone-link block and the corresponding branch around the other reported
location, to serialize anchor children through the existing inline extractor
instead of using link.textContent. Wrap the extracted HTML and Markdown in the
link destination while preserving rich child elements such as images, bold text,
and emphasis; retain the current URL fallback only when the extracted content is
empty.
- Around line 610-624: Validate link URLs in both anchor-export paths, including
the standalone-link branch around the visible `tagName === 'a'` logic and the
other anchor branch near the corresponding export code, before adding them to
`htmlParts` or `textParts`. Allow only intended schemes such as `http` and
`https`; skip or safely handle disallowed schemes like `javascript:` while
preserving existing label and escaping behavior for valid links.
- Around line 610-624: Update the standalone-link handling in the extraction
method to resolve relative href values against link.ownerDocument.baseURI before
generating either HTML or Markdown output, using the resolved destination
consistently for both paths. Apply the same normalization in the corresponding
link serialization logic around the later link-handling block.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7255be61-b37c-4558-87ea-2d2521087360

📥 Commits

Reviewing files that changed from the base of the PR and between 1f0e159 and 096a5f8.

📒 Files selected for processing (5)
  • src/features/export/services/DOMContentExtractor.ts
  • src/features/export/services/__tests__/DOMContentExtractor.test.ts
  • src/features/plugins/builtin/chatgptExport/exporter.test.ts
  • src/features/plugins/builtin/chatgptExport/tempHandoff.test.ts
  • src/features/plugins/builtin/chatgptExport/tempHandoff.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/features/plugins/builtin/chatgptExport/exporter.test.ts
  • src/features/plugins/builtin/chatgptExport/tempHandoff.ts

Comment thread src/features/export/services/DOMContentExtractor.ts Outdated
Comment thread src/features/export/services/DOMContentExtractor.ts
Comment thread src/features/export/services/DOMContentExtractor.ts
Co-authored-by: Codex <codex@users.noreply.github.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 37bb145ada

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/features/plugins/builtin/chatgptExport/index.ts
Comment thread src/features/plugins/builtin/chatgptExport/conversation.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/tempHandoff.ts Outdated
Co-authored-by: Codex <codex@users.noreply.github.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: daab5703cb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +373 to +377
export function extractSnapshotMarkdown(message: ChatGptMessageSnapshot): string {
const extracted =
message.role === 'user'
? DOMContentExtractor.extractUserContent(message.element)
: DOMContentExtractor.extractAssistantContent(message.element);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve ChatGPT formulas during rich extraction

When a ChatGPT message contains current KaTeX markup, this still routes it through DOMContentExtractor, whose math branches recognize Gemini's data-math/.math-inline shapes but not ChatGPT's [data-math-source] and .katex-display shapes. Fresh evidence after the earlier rich-extractor review is FormulaCopyService.extractLatexSource, which explicitly handles current ChatGPT markup, together with its fixtures at lines 562-619; without equivalent handling here, Markdown/JSON flatten formulas to rendered glyph text and PDF loses the semantic KaTeX structure. Reuse that established extraction path for both inline and display math.

AGENTS.md reference: AGENTS.md:L73-L73

Useful? React with 👍 / 👎.

Comment on lines +307 to +310
const maximum = Math.max(0, target.scrollHeight - target.clientHeight);
if (target.scrollTop >= maximum - 2) {
break;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Wait for an active response to finish before exporting

When the user starts a whole-conversation export while ChatGPT is still generating the bottom response, reaching the current scroll maximum breaks this loop immediately; the only subsequent delay is one settleMs sample, with no generation-state or content-stability check. Any response that continues beyond that roughly 120 ms window is silently exported as a partial answer, so wait for generation completion or explicitly reject an in-progress export before treating the bottom as complete.

Useful? React with 👍 / 👎.

Comment on lines +273 to +275
const file = new File([delivery.attachment], delivery.filename, { type: 'text/markdown' });
if (!(await dispatchAttachmentAndVerify(input, file))) return false;
return insertComposerText(input, delivery.directive);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Track partial attachment delivery before retrying

If the attachment paste succeeds and creates a preview but inserting the directive fails, this returns false after modifying the composer and leaves the pending payload for retry. The next resumePendingHandoff attempts the attachment again, but dispatchAttachmentAndVerify can never report success once alreadyVisible is true because every success check is guarded by !alreadyVisible; retries can therefore add duplicate files while the handoff remains stuck until its TTL expires. Persist the attachment-complete stage or make retries deliver only the missing directive.

Useful? React with 👍 / 👎.

Co-authored-by: Codex <codex@users.noreply.github.com>
@TanChuping

Copy link
Copy Markdown
Author

The 80% docstring warning is not adopted. That threshold is not part of this repository's checks, and boilerplate docstrings on local plugin internals would add noise without improving a public API.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a5c6960b1a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +90 to +92
const extracted = this.extractAssistantContent(element);
extracted.attachments = attachments;
return extracted;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include mixed ChatGPT attachments in delegated exports

When a ChatGPT user turn contains both prompt text in a nested .markdown container and a sibling [data-testid="file-attachment"] pill, extractAssistantContent() narrows extraction to the Markdown container and excludes the pill. This branch then records the attachment only in extracted.attachments, but the Markdown/JSON exporters consume text and the PDF exporter consumes html, so the uploaded filename disappears from every format. Fresh evidence after the earlier attachment review is that the new ChatGPT selector populates this otherwise-unserialized array; append the detected attachments to the delegated text and HTML as the Gemini path does.

Useful? React with 👍 / 👎.

Comment on lines +169 to +171
const host = turn || element;
const virtualPosition = readVirtualPosition(host);
if (virtualPosition) return `fallback-${role}-${virtualPosition.key}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stabilize fallback IDs across history prepends

For the supported fallback turn structures without native IDs, the ID is derived from the element's current global scroll offset. When reaching the top asynchronously prepends older history, every previously mounted turn shifts to a new offset; snapshots captured before the prepend therefore retain the old IDs while the same turns are collected again under new IDs. Whole-conversation exports can consequently contain duplicates and place the formerly oldest turn before newly loaded older messages, so identity must remain stable across prepend-induced layout shifts or collection must avoid retaining pre-stabilization fallback snapshots.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot mentioned this pull request Aug 9, 2026
10 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant