Skip to content

feat(plugins): add ChatGPT temporary chat handoff - #921

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

feat(plugins): add ChatGPT temporary chat handoff#921
TanChuping wants to merge 20 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 saving a temporary ChatGPT conversation and continuing it in a normal chat.
  • Download a uniquely named Markdown backup before leaving temporary mode.
  • Put the transcript (or a Markdown attachment for long conversations) into the normal-chat composer as a draft. Nothing is sent automatically.
  • Preserve ChatGPT account scope, the existing composer draft, and pending handoff state across SPA or full-page navigation, while cancelling cleanly when the plugin is disabled.
  • Block the handoff while the current draft has an unsent file/image attachment, preventing ChatGPT from discarding it when temporary mode closes.

Scope after #920

#920 provides the shared ChatGPT conversation exporter. This PR is therefore limited to the unique temporary-chat handoff feature instead of maintaining a second Markdown/JSON/PDF exporter. It reuses #920's collector and ships under a separate plugin ID, so export and handoff can be enabled together.

Thanks so much to @TanChuping for the original implementation and for identifying this useful workflow.

Three-round SOL/MAX cold-read review

Tested tree: 188c944

Three independent SOL/MAX cold-read reviews were run sequentially. Each round reviewed the implementation after the prior round's fixes; the third round read the complete temporary-chat handoff feature rather than only the diff. A separate three-pass final maintainer review followed before push.

Scope P0 P1 P2 P3 Total
Three SOL/MAX reviews 0 6 11 2 19
Final maintainer review 0 0 1 0 1
Combined 0 6 12 2 20

Disposition: 17 fully fixed, 2 proportionally addressed with an explicit retained boundary, and 1 not adopted after trigger/impact/cost analysis.

Review Priority Finding Resolution
SOL/MAX 1 P1 Attachment DOM mutations could retrigger delivery and paste the same file repeatedly. Fixed: resume is gated on the exact pending attachment preview and protected by a one-shot latch.
SOL/MAX 1 P1 The 60-second TTL was enforced only when a page read the pending record, so closing the tab could leave it behind. Fixed: background alarms enforce expiry, startup reconciles pending records, and failed deletion is retried.
SOL/MAX 1 P1 Unload state could reset before the abort path recorded why the operation stopped. Fixed: lifecycle state follows scoped pagehide/pageshow, and the abort reason is captured synchronously.
SOL/MAX 1 P2 A reused composer could place the preserved draft before the handoff context. Fixed: handoff context is inserted first and the original draft remains a separate trailing segment.
SOL/MAX 1 P2 The auxiliary link received initial dialog focus. Fixed: initial focus goes to Cancel while preserving focus trapping and restoration.
SOL/MAX 1 P2 Localized actions could overflow between 361px and 520px. Fixed: responsive wrapping and narrow-width layout rules cover the full range.
SOL/MAX 2 P1 An unsent draft had no independent recovery source after pending state expired. Fixed: the downloaded Markdown backup includes a localized unsent-draft section.
SOL/MAX 2 P1 On an unscoped route, the implementation cannot cryptographically prove that the user did not deliberately switch ChatGPT accounts during the <=60-second pending window. Not adopted: /u/N route changes are already rejected. Covering a deliberate unscoped-account switch would require account APIs or another cross-page confirmation state and is disproportionate to a non-normal flow.
SOL/MAX 2 P1 Attachment upload could replace the editor while later content was still written to the detached node. Fixed: delivery reacquires and verifies the live composer after upload.
SOL/MAX 2 P2 A failed storage deletion could still cancel its expiry alarm. Fixed: the alarm is cancelled only after successful deletion; background cleanup retries failures.
SOL/MAX 2 P2 A short draft could be mistaken for a substring inside unrelated composer text. Fixed: idempotence checks require a complete line-bounded segment.
SOL/MAX 2 P2 A cancelled beforeunload sequence could leave sticky navigation state. Fixed: only actual pagehide/pageshow lifecycle events control unload state.
SOL/MAX 2 P2 Composer mutation handling enumerated all extension-local storage. Proportionally addressed: page hot paths read only the current tab's exact key. One background cold-start reconciliation scan remains intentionally; a second persistent index would create more synchronization state than it removes.
SOL/MAX 3 P2 Multiline user prompts were flattened in the generated transcript. Fixed: extraction preserves text-node line breaks, <br> elements, and block boundaries.
SOL/MAX 3 P2 Leaving temporary mode could discard an unsent file/image draft; previously sent binary files cannot be reconstructed from the rendered DOM. Proportionally addressed: a live unsent attachment blocks handoff with a localized explanation, so temporary mode remains intact. Historical attachment names remain in exported context. Binary refetch/reupload was not guessed because original bytes are not reliably exposed by the page.
SOL/MAX 3 P2 Pending state was cleared immediately after insertion even if ChatGPT replaced the accepted composer shortly afterward. Fixed: delivery is observed through a stabilization window; a replacement composer is redelivered and reverified before pending state is cleared.
SOL/MAX 3 P2 Fallback navigation lost the active custom-GPT route. Fixed: both /g/<gpt> and /u/N/g/<gpt> contexts are preserved.
SOL/MAX 3 P3 Generated handoff artifacts were fully localized only in English and Chinese. Fixed: all user-visible handoff titles, instructions, transcript boundaries, role labels, draft headings, and attachment warnings cover all 10 supported languages.
SOL/MAX 3 P3 The dialog body was not connected through aria-describedby. Fixed and regression-tested.
Final review P2 The requested Simplified Chinese feature name had regressed from 临时对话反悔 to 临时对话续接. Fixed: 临时对话反悔 is restored, Traditional Chinese uses 暫時對話反悔, and both are locked by tests.

Verification

Check Result
Focused feature/shared-adapter tests 56/56 passed
Full test suite 2787/2788 passed; the only failure is the pre-existing Windows temporary-path handling in verify-release-privacy.test.ts for signed-app embedded.provisionprofile files
TypeScript Passed
Locale consistency Passed: all 10 locales, 697 keys
ESLint 0 errors; 4 pre-existing console warnings in the background entrypoint
Formatting / whitespace Prettier and git diff --check passed
Production builds Chrome, Firefox, and Safari passed
Visual QA Desktop light/dark and responsive 390px/320px layouts checked

Checklist

  • The plugin is opt-in and ChatGPT-only.
  • The shared exporter is reused rather than duplicated.
  • Lifecycle cleanup, account-route changes, delayed composer mounts/replacements, attachment failure, unique filenames, and formatted-draft preservation have regression coverage.
  • No content is sent automatically.

Follow-up to #920. Preserves the unique handoff portion of the original #921 contribution.

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

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 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 virtualized scroll windows, builds ordered turns, extracts content, and creates metadata.
Export payloads and content extraction
src/features/plugins/builtin/chatgptExport/exporter.ts, src/features/export/services/DOMContentExtractor.ts, src/features/formulaCopy/FormulaCopyService.ts, src/features/plugins/builtin/chatgptExport/exporter.test.ts, src/features/export/services/__tests__/DOMContentExtractor.test.ts
Generates Markdown and JSON payloads, delegates PDF export, and preserves links, attachments, formulas, 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, src/features/plugins/builtin/chatgptExport/*test.ts
Adds export-button injection, menus, dialogs, progress, toasts, lifecycle cleanup, localization, responsive styling, manifest data, 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

Possibly related issues

Possibly related PRs

  • Nagi-ovo/voyager#865: Extends the ChatGPT export functionality introduced in this PR.
  • Nagi-ovo/voyager#919: Extends the ChatGPT export implementation with enhanced PDF styling and extraction fallbacks.
  • Nagi-ovo/voyager#920: Extends the ChatGPT export implementation with dedicated conversation collection and export flows.

Suggested reviewers: nagi-ovo

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

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.
Description check ⚠️ Warning The description explains the feature and verification, but it omits required Related Issue, Visual Proof, Browser Testing, and full Checklist sections. Add the required template sections, link the related issue, document browsers actually tested, include visual proof, and complete or explain each checklist item.
✅ Passed checks (3 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 accurately identifies the temporary ChatGPT handoff feature, which is a major part of the changeset.
✨ 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 Outdated
Comment thread src/features/export/services/PDFPrintService.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/conversation.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/conversation.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/exporter.test.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/styles.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/tempHandoff.test.ts Outdated
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 Outdated

@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 Outdated
Comment thread src/features/plugins/builtin/chatgptExport/exporter.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/i18n.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: 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 Outdated
Comment thread src/features/plugins/builtin/chatgptTemporaryHandoff/handoff.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 Outdated

@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 Outdated
Comment thread src/features/export/services/DOMContentExtractor.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: 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 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: 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 thread src/features/plugins/builtin/chatgptExport/conversation.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/conversation.ts Outdated
Comment thread src/features/plugins/builtin/chatgptTemporaryHandoff/handoff.ts Outdated
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 thread src/features/export/services/DOMContentExtractor.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/conversation.ts Outdated
@coderabbitai coderabbitai Bot mentioned this pull request Aug 9, 2026
10 tasks
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

🧹 Nitpick comments (3)
src/features/export/services/DOMContentExtractor.ts (1)

486-496: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Run the formula marker test before FormulaCopyService.extractLatexSource. Both extraction paths call extractLatexSource on every element node, and that helper performs closest plus up to two subtree querySelector scans. The cheap class/attribute test that gates the branch runs afterwards, so non-math subtrees are scanned repeatedly during export.

  • src/features/export/services/DOMContentExtractor.ts#L486-L496: compute the math-block / katex-display / data-math / data-math-source test first in processNodes, then call extractLatexSource only when it passes.
  • src/features/export/services/DOMContentExtractor.ts#L888-L897: apply the same ordering in processInlineContent, where the recursion repeats the scan at each nesting level.
🤖 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/DOMContentExtractor.ts` around lines 486 - 496,
In src/features/export/services/DOMContentExtractor.ts lines 486-496 within
processNodes, evaluate the math-block/katex-display/data-math/data-math-source
marker before calling FormulaCopyService.extractLatexSource, and only extract
when the marker passes. Apply the same ordering in processInlineContent at lines
888-897 so both recursive paths avoid scanning non-math elements.
src/features/plugins/builtin/chatgptExport/tempHandoff.test.ts (1)

139-149: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Dispose each PluginScope from a teardown hook.

Each test calls await scope.dispose() as the last statement. If an assertion fails earlier, the scope stays live. Its listeners, timers, and observers then leak into the following tests and can produce cascading failures that hide the original one. Register the scope in a shared list and dispose it in afterEach.

♻️ Proposed refactor
+const scopes: PluginScope[] = [];
+
+function createScope(): PluginScope {
+  const scope = new PluginScope();
+  scopes.push(scope);
+  return scope;
+}
+
+afterEach(async () => {
+  for (const scope of scopes.splice(0)) await scope.dispose();
+});

Then replace new PluginScope() with createScope() and remove the trailing await scope.dispose() calls.

Also applies to: 178-193

🤖 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/plugins/builtin/chatgptExport/tempHandoff.test.ts` around lines
139 - 149, Ensure every PluginScope created in the tests is disposed from an
afterEach teardown hook, including scopes created in the test blocks around
addComposer and handoffTemporaryChat. Register scopes through a shared
createScope helper backed by a list, replace direct new PluginScope() calls with
createScope(), and remove trailing await scope.dispose() statements.
src/features/formulaCopy/FormulaCopyService.ts (1)

186-186: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Move extractLatexSource to a standalone helper module.

Import the helper directly from DOMContentExtractor, and re-export it from FormulaCopyService.ts to preserve existing callers. This avoids loading temml, webextension-polyfill, logger, and i18n dependencies for the two extractor call sites.

🤖 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/formulaCopy/FormulaCopyService.ts` at line 186, Move
extractLatexSource out of the FormulaCopyService class into a standalone helper
module, then import that helper directly in DOMContentExtractor. Re-export the
helper from FormulaCopyService.ts so existing callers remain compatible, without
retaining the heavy service dependencies in the extractor paths.
🤖 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 393-406: Update DOMContentExtractor.extractLinkLabel to normalize
and collapse whitespace in the resolved link text before constructing the
returned html and text values, while preserving the existing inline-content and
fallback behavior.

---

Nitpick comments:
In `@src/features/export/services/DOMContentExtractor.ts`:
- Around line 486-496: In src/features/export/services/DOMContentExtractor.ts
lines 486-496 within processNodes, evaluate the
math-block/katex-display/data-math/data-math-source marker before calling
FormulaCopyService.extractLatexSource, and only extract when the marker passes.
Apply the same ordering in processInlineContent at lines 888-897 so both
recursive paths avoid scanning non-math elements.

In `@src/features/formulaCopy/FormulaCopyService.ts`:
- Line 186: Move extractLatexSource out of the FormulaCopyService class into a
standalone helper module, then import that helper directly in
DOMContentExtractor. Re-export the helper from FormulaCopyService.ts so existing
callers remain compatible, without retaining the heavy service dependencies in
the extractor paths.

In `@src/features/plugins/builtin/chatgptExport/tempHandoff.test.ts`:
- Around line 139-149: Ensure every PluginScope created in the tests is disposed
from an afterEach teardown hook, including scopes created in the test blocks
around addComposer and handoffTemporaryChat. Register scopes through a shared
createScope helper backed by a list, replace direct new PluginScope() calls with
createScope(), and remove trailing await scope.dispose() statements.
🪄 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: 8f25a327-82a3-46fe-8d10-bfad9bb4201f

📥 Commits

Reviewing files that changed from the base of the PR and between 37bb145 and f9ba16e.

📒 Files selected for processing (7)
  • src/features/export/services/DOMContentExtractor.ts
  • src/features/export/services/__tests__/DOMContentExtractor.test.ts
  • src/features/formulaCopy/FormulaCopyService.ts
  • src/features/plugins/builtin/chatgptExport/conversation.test.ts
  • src/features/plugins/builtin/chatgptExport/conversation.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/tempHandoff.ts
  • src/features/plugins/builtin/chatgptExport/conversation.ts

Comment thread src/features/export/services/DOMContentExtractor.ts Outdated

@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: f9ba16e526

ℹ️ 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/selectionMode.ts Outdated
Comment thread src/features/plugins/builtin/chatgptExport/tempHandoff.ts Outdated
TanChuping and others added 3 commits August 10, 2026 15:24
Co-authored-by: Codex <codex@users.noreply.github.com>
Co-authored-by: Codex <codex@users.noreply.github.com>
@TanChuping
TanChuping force-pushed the feat/chatgpt-export-plugin branch from f414867 to 72ad036 Compare August 10, 2026 07:34
@TanChuping

Copy link
Copy Markdown
Author

Addressed the three folded review nits in \72ad036: formula extraction now runs only for marked math nodes, test scopes are disposed from \�fterEach, and the pure LaTeX-source helper is split out so DOM export no longer imports the heavier formula-copy service. The existing static API remains compatible.

@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: 72ad036f03

ℹ️ 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/selectionMode.ts Outdated
Comment thread src/features/plugins/builtin/chatgptTemporaryHandoff/handoff.ts Outdated
@TanChuping

Copy link
Copy Markdown
Author

@codex review

Please review the current HEAD (fa6165d) comprehensively and batch every remaining actionable finding into this single pass. Do not drip-feed additional issues one commit at a time; the serial review loop is wasting contributor time.

@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: fa6165d832

ℹ️ 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 Outdated
Comment thread src/features/plugins/builtin/chatgptExport/index.ts Outdated
@TanChuping

Copy link
Copy Markdown
Author

Review boundary: the Codex reviewer responded to the explicitly requested comprehensive one-pass review with the two findings above; both are now addressed in 7003e4d with regression coverage. Treat that pass as complete. Do not restart the serial drip-feed loop by posting additional incremental findings against commits whose sole purpose is addressing this batch. Any later observations belong to a separate maintainer-requested review cycle and will be treated as out of scope here.

@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: 7003e4dd93

ℹ️ 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/export/services/DOMContentExtractor.ts Outdated
@TanChuping

Copy link
Copy Markdown
Author

Review boundary: these two findings were on code that predates 7003e4d and therefore arrived after the prior one-pass review was explicitly declared complete. Both are nevertheless fixed in e00eb0a with regression coverage, and the threads are resolved. This review cycle is closed; please do not continue with serial incremental findings unless a maintainer explicitly requests a new review round.

@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: e00eb0a401

ℹ️ 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 Outdated
Comment thread src/features/plugins/builtin/chatgptExport/tempHandoff.ts Outdated
Nagi-ovo and others added 2 commits August 13, 2026 00:23
Rework Nagi-ovo#921 to preserve its unique temporary-chat handoff without duplicating the conversation exporter added by Nagi-ovo#920.

Co-authored-by: Tan Chuping <81346625+DaltonWritingCenter@users.noreply.github.com>

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

Copy link
Copy Markdown
Collaborator

Hi @TanChuping, thanks for your interest in contributing!

Voyager automatically admits focused bug fixes when the PR title uses fix: ... or fix(scope): ... and the patch changes no more than 20 files and 1,000 total added or deleted lines.

This PR does not use a fix: ... title, so it needs maintainer alignment first. If it fixes a bug, rename it to a conventional fix: ... or fix(scope): ... title and tag a maintainer to review and reopen it.

Feature PRs require prior maintainer alignment: please open a GitHub Discussion or issue describing the change first. Contributors listed in .github/APPROVED_CONTRIBUTORS bypass this automatic intake gate, but membership does not grant maintainer authority or guarantee acceptance.

If this gate classified the pull request incorrectly, reply and tag a maintainer listed in .github/MAINTAINERS. A verified maintainer can reopen it; reopening by anyone else will be closed again automatically.

Patch size: 94 changed files, 9465 changed lines.

See https://github.com/Nagi-ovo/voyager/blob/main/.github/CONTRIBUTING.md for the contribution policy.

@Nagi-ovo Nagi-ovo changed the title feat(plugins): add ChatGPT conversation export feat(plugins): add ChatGPT temporary chat handoff Aug 12, 2026
@Nagi-ovo

Copy link
Copy Markdown
Owner

Thanks so much for the original handoff work. I narrowed the PR after #920 and pushed the tested implementation successfully. One GitHub-only cleanup remains: because the fast-forward update placed current main on the first-parent side, GitHub is still displaying the old 94-file merge-base even though the actual head tree differs from main in only 11 files. I prepared a lineage-only merge commit that changes no source tree and restores the focused diff, but “Allow edits by maintainers” is now disabled, so the fork rejected it. Could you please re-enable maintainer edits briefly? I will push only that ancestry cleanup and then leave the branch alone.

@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: 64955bd012

ℹ️ 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 +287 to +290
return (
insertTextIntoChatInput(existing ? `\n\n${text}` : text, input) &&
readComposerText(input).includes(text)
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Normalize contenteditable line breaks before verifying handoff

When ChatGPT uses its normal contenteditable composer, every planned inline handoff and attachment directive contains newlines, but the insertion helper represents those breaks as paragraphs or <br> elements while textContent concatenates their text without newline characters. Consequently this includes(text) check reports delivery-failed even after visibly inserting the handoff, retains the pending payload, and can duplicate it on a later retry. Verify normalized editor text/DOM state rather than comparing the original multiline string directly.

Useful? React with 👍 / 👎.

@Nagi-ovo Nagi-ovo left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for sticking with this. The plugin boundary and reuse of the shared #920 collector now look right, and CI is green. I found four blockers in the handoff/recovery path; the inline comments include deterministic reproductions and the regression cases we need before merge.


function currentComposer(excluded?: HTMLElement): HTMLElement | null {
return (
Array.from(document.querySelectorAll<HTMLElement>(COMPOSER_SELECTOR)).find(

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This combined selector returns the first match in DOM order, not #prompt-textarea first. I reproduced a page with another contenteditable[role=textbox] before the real prompt: the handoff was inserted into that editor, the real prompt stayed empty, and pending state was cleared as ready. Please resolve strong composer selectors sequentially and constrain any generic fallback to the actual ChatGPT composer; add a competing-editor regression test.

toggle.click();
for (let attempt = 0; attempt < 16; attempt += 1) {
if (!isTemporaryChat()) {
const replacement = currentComposer(temporaryComposer);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This assumes leaving temporary mode replaces the composer DOM node, but ChatGPT can reuse the same #prompt-textarea during SPA reconciliation. The exact production path then waited about 4.7s, clicked New Chat once, and returned null even though a usable normal composer was present. Please determine readiness from temporary-mode/route state instead of node identity, and add a same-node-reuse test.

}

function writePending(delivery: HandoffDelivery, accountScope: string): void {
sessionStorage.setItem(

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This persists the full temporary-chat transcript in ChatGPT page-owned sessionStorage. That storage is not extension-private, and failure paths can leave the payload there until a later read or tab close. Please follow the existing fork handoff precedent and keep pending content in extension-owned storage with TTL and removal on success/cancel/expiry; also handle storage-write failure before any full-page navigation.

return 'account-mismatch';
}
if (isTemporaryChat()) return null;
const input = await findComposer(scope, 6_000);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Abort during resumePendingHandoff does not clear pending state. I reproduced disabling the plugin while this wait was active, then re-enabling it within the TTL: the old transcript was inserted automatically. Since the plugin promises clean cancellation, please clear or otherwise invalidate the pending operation on plugin disposal and add a disable-mid-resume regression test.

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: 6a234a3b1d

ℹ️ 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/chatgptTemporaryHandoff/handoff.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: f6c80bb827

ℹ️ 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/chatgptTemporaryHandoff/handoff.ts
Comment thread src/features/plugins/builtin/chatgptTemporaryHandoff/handoff.ts Outdated
Comment thread src/features/plugins/builtin/chatgptTemporaryHandoff/index.ts
Comment thread src/features/plugins/builtin/chatgptTemporaryHandoff/handoff.ts Outdated
@TanChuping
TanChuping requested a review from Nagi-ovo August 14, 2026 10:36
@TanChuping

Copy link
Copy Markdown
Author

Three-round SOL/MAX cold-read review and verification

Commit 188c944 contains the follow-up fixes from three independent, sequential SOL/MAX cold-read reviews. Each round reviewed the implementation after the previous round's fixes; the third round read the complete temporary-chat handoff feature rather than only the diff. A separate three-pass final maintainer review was then performed before push.

Priority summary

Scope P0 P1 P2 P3 Total
Three SOL/MAX reviews 0 6 11 2 19
Final maintainer review 0 0 1 0 1
Combined 0 6 12 2 20

Disposition: 17 fully fixed, 2 proportionally addressed with an explicit retained boundary, and 1 not adopted after trigger/impact/cost analysis.

Findings and disposition

Review Priority Finding Resolution
SOL/MAX 1 P1 Attachment DOM mutations could retrigger delivery and paste the same file repeatedly. Fixed: resume is gated on the exact pending attachment preview and protected by a one-shot latch.
SOL/MAX 1 P1 The 60-second TTL was enforced only when a page read the pending record, so closing the tab could leave it behind. Fixed: background alarms now enforce expiry, startup reconciles pending records, and failed deletion is retried.
SOL/MAX 1 P1 Unload state could reset before the abort path recorded why the operation stopped, causing a valid pending handoff to be deleted. Fixed: lifecycle state now follows scoped pagehide/pageshow, and the abort reason is captured synchronously.
SOL/MAX 1 P2 When ChatGPT reused the composer, the preserved draft could appear before the handoff context. Fixed: handoff context is inserted first and the user's original draft remains as a separate trailing segment.
SOL/MAX 1 P2 The auxiliary link received initial dialog focus. Fixed: initial focus goes to Cancel while the dialog keeps its focus trap and restoration behavior.
SOL/MAX 1 P2 Localized actions could overflow between 361px and 520px. Fixed: responsive wrapping and narrow-width layout rules cover the full range.
SOL/MAX 2 P1 An unsent draft had no independent recovery source after the pending handoff expired. Fixed: the downloaded Markdown backup now includes a localized unsent-draft section.
SOL/MAX 2 P1 On an unscoped route, the implementation cannot cryptographically prove that the user did not deliberately switch ChatGPT accounts during the <=60-second pending window. Not adopted: /u/N route changes are already rejected. Covering the deliberate unscoped-account-switch case would require account APIs or another cross-page confirmation state and is disproportionate to a non-normal flow.
SOL/MAX 2 P1 Attachment upload could replace the editor while the directive and draft were still being written to the detached node. Fixed: delivery reacquires and verifies the live composer after upload.
SOL/MAX 2 P2 A failed storage deletion could still cancel the expiry alarm. Fixed: the alarm is cancelled only after successful deletion; background cleanup retries failures.
SOL/MAX 2 P2 A short draft could be mistaken for a substring inside unrelated composer text. Fixed: idempotence checks now require a complete line-bounded segment.
SOL/MAX 2 P2 A cancelled beforeunload sequence could leave sticky navigation state. Fixed: only actual pagehide/pageshow lifecycle events control unload state.
SOL/MAX 2 P2 Composer mutation handling enumerated all extension-local storage. Proportionally addressed: page hot paths now read only the current tab's exact key. One background cold-start reconciliation scan remains intentionally; adding a second persistent index would create more synchronization state than it removes.
SOL/MAX 3 P2 Multiline user prompts were flattened in the generated transcript. Fixed: ChatGPT user extraction now preserves text-node line breaks, <br> elements, and block boundaries.
SOL/MAX 3 P2 Leaving temporary mode could discard an unsent file/image draft; previously sent binary files cannot be reconstructed from the rendered DOM. Proportionally addressed: a live unsent attachment blocks handoff with a localized explanation, so temporary mode remains intact. Historical attachment names remain in exported context. Binary refetch/reupload was intentionally not guessed because the original bytes are not reliably exposed by the page.
SOL/MAX 3 P2 Pending state was cleared immediately after insertion even if ChatGPT replaced the accepted composer shortly afterward. Fixed: delivery is observed through a stabilization window; a replacement composer is redelivered and reverified before pending state is cleared.
SOL/MAX 3 P2 Fallback navigation lost the active custom-GPT route. Fixed: both /g/<gpt> and /u/N/g/<gpt> contexts are preserved.
SOL/MAX 3 P3 Generated handoff artifacts were fully localized only in English and Chinese. Fixed: all user-visible handoff titles, instructions, transcript boundaries, role labels, draft headings, and attachment warnings cover all 10 supported languages.
SOL/MAX 3 P3 The dialog body was not connected through aria-describedby. Fixed and regression-tested.
Final review P2 The requested Simplified Chinese feature name had regressed from 临时对话反悔 to 临时对话续接. Fixed: 临时对话反悔 is restored, Traditional Chinese uses 暫時對話反悔, and both are locked by tests.

Verification

Check Result
Focused feature/shared-adapter tests 56/56 passed
Full test suite 2787/2788 passed; the only failure is the pre-existing Windows temporary-path handling in verify-release-privacy.test.ts for signed-app embedded.provisionprofile files
TypeScript Passed
Locale consistency Passed: all 10 locales, 697 keys
ESLint 0 errors; 4 pre-existing console warnings in the background entrypoint
Formatting / whitespace Prettier and git diff --check passed
Production builds Chrome, Firefox, and Safari passed
Visual QA Desktop light/dark and responsive 390px/320px layouts checked

@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: 188c9442fb

ℹ️ 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/chatgptTemporaryHandoff/handoff.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants