Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions src/features/export/services/DOMContentExtractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ export class DOMContentExtractor {

// Extract text from query-text-line paragraphs
const textLines = element.querySelectorAll('.query-text-line');
if (images.length === 0 && attachments.length === 0 && textLines.length === 0) {
// ChatGPT and other hosts use ordinary semantic HTML rather than Gemini's
// query-text-line/file-preview elements. Reuse the standards-aware rich
// fallback so paragraphs, links, images, and file pills are retained.
return this.extractAssistantContent(element);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
const textParts: string[] = [];
textLines.forEach((line) => {
const el = line as HTMLElement;
Expand Down Expand Up @@ -447,6 +453,21 @@ export class DOMContentExtractor {
}
}

// Standard Markdown renderers, including ChatGPT, emit bare <pre><code>
// blocks rather than Gemini's code-block custom element. Consume the code
// here and mark it so the later compatibility scan cannot emit it twice.
if (tagName === 'pre') {
const codeElement = child.querySelector<HTMLElement>(':scope > code');
if (codeElement) {
const extracted = this.extractCodeFromCodeElement(codeElement);
(codeElement as Element & { processedByGV?: boolean }).processedByGV = true;
flags.hasCode = true;
htmlParts.push(extracted.html);
textParts.push(`\n${extracted.text}\n`);
continue;
}
}

Comment thread
coderabbitai[bot] marked this conversation as resolved.
const exportCodeBlocks = this.findExportCodeBlocks(child);
const directExportCodeBlock = exportCodeBlocks.find(({ element }) => element === child);
if (directExportCodeBlock) {
Expand Down Expand Up @@ -586,6 +607,21 @@ export class DOMContentExtractor {
continue;
}

// Standalone links (for example ChatGPT attachment pills) are block-level
// children in some message layouts, so preserve their destination here.
if (tagName === 'a') {
const link = child as HTMLAnchorElement;
const href = link.getAttribute('href') || link.href;
Comment thread
TanChuping marked this conversation as resolved.
Outdated
const label = this.normalizeText(link.textContent || '') || href;
if (href) {
htmlParts.push(
`<a href="${this.escapeHtmlAttribute(href)}">${this.escapeHtml(label)}</a>`,
);
textParts.push(`[${label.replace(/([\\\]])/g, '\\$1')}](${href.replace(/\)/g, '\\)')})`);
continue;
}
}

Comment thread
coderabbitai[bot] marked this conversation as resolved.
// Paragraph with possible inline formulas
if (tagName === 'p') {
const processed = this.processInlineContent(child as HTMLElement);
Expand Down Expand Up @@ -830,6 +866,22 @@ export class DOMContentExtractor {
return;
}

// Links and linked attachment labels
if (el.tagName === 'A') {
const link = el as HTMLAnchorElement;
const href = link.getAttribute('href') || link.href;
const label = this.normalizeText(link.textContent || '') || href;
if (href) {
htmlParts.push(
`<a href="${this.escapeHtmlAttribute(href)}">${this.escapeHtml(label)}</a>`,
);
textParts.push(
`[${label.replace(/([\\\]])/g, '\\$1')}](${href.replace(/\)/g, '\\)')})`,
);
return;
}
}

// Inline images
if (el.tagName === 'IMG') {
const imgEl = el as HTMLImageElement;
Expand Down
Loading
Loading