Universal Markdown Exporter v2.2.0
Critical Bugs Fixed
1. extractDRCitations - Typo and broken selector
The v2.1.0 code had a fatal typo on line 242: urlUrl.getAttribute('href') instead of urlEl.getAttribute('href'). This would cause a crash during citation extraction attempts. Additionally, the selector strategy was restructured as follows:
- Citation groups (
div.flex.flex-col.gap-0) are now queried first, falling back to direct button[aria-label^="Open source"] queries as necessary.
- Each citation button contains its own
a[href] with the individual source URL.
- Confirmed via DOM API testing that React's
createElement-based DOM allows nested buttons to work correctly (unlike HTML parser behavior in JSDOM).
2. extractDRScanned - Broken structure assumption
The v2.1.0 code assumed scanned items had individual URLs. Audit of the real HTML revealed:
- Scanned sources are grouped by domain (206 domain groups for 554 items)
- Each domain group has ONE
a[href] at the domain level
- Individual scanned items have only titles and snippets, no individual URLs
- Rewrote to: iterate domain groups -> get domain href -> list each item's title linked to domain URL, with snippet as sub-item
3. extractDRActivity - Broken selectors and wrong entry type handling
- Title selector used
'.text-token-text-primary.text-\\[14px\\]' -- the escaped bracket syntax doesn't work in querySelector. Changed to .text-token-text-primary (sufficient and correct).
- Body selector
.text-token-text-secondary.mt-2 was wrong for "Searching" entries which use .mt-1 for their link container.
- The code assumed all "Searching" entries have links. Audit revealed two types:
- Bare "Searching" (19 entries): have links in
div.mt-1 > div.flex-wrap > a[href], no body text.
- "Searching for X" (6 entries): have body text in
.text-token-text-secondary.mt-2, no links.
- Rewrote to handle both cases correctly, with proper link extraction from the
.mt-1 container.
4. extractDRDuration - Numbers were garbled from animated spans
The duration div uses animated CSS number roller spans (span[role="img"]). Calling .textContent on these returns all digits 0-9 concatenated. Fixed to use aria-label attributes which contain the clean numeric values (verified: aria-label="n" for citations, aria-label="n" for searches).
Element Picker Fixes
1. mousemove handler - elementFromPoint accuracy
The v2.1.0 code hid only the tip element before calling elementFromPoint. But the .h2m-sel outline on the currently selected element could also interfere with hit-testing. Now temporarily removes the .h2m-sel class from the current element before elementFromPoint and restores it after.
2. mousedown handler - DR-aware click behavior
Previously, clicking any element inside the DR overlay would just convert that single element to markdown (often a single paragraph). Now detects if the clicked element is inside a DR overlay via isDRElement() and automatically triggers the full autoExportDR() flow with all panel extractors.
Enhancements
1. Gemini extraction - Added extractGemini() function for gemini.google.com conversations and canvases, with G keyboard shortcut.
2. Obsidian export - Added purple "Obsidian" button to the modal toolbar with obsidian://advanced-uri integration, plus a configuration dialog for vault name and folder.
3. stripUtm helper - Centralized UTM parameter stripping to avoid repetition across extractors.
Output Format Alignment
The extractors now produce markdown matching the following template:
Citations [x Sources]
- citation 1
...
Scanned [y Sources]
- source 1
- content 1 if present
...
Thinking Activity [Research completed in z]
- thought subtitle 1**
- thought summary 1
...
zx. Searching
- citation title 1
- citation title 2
- citation title 3
...
zx. citation title zx
...
Universal Markdown Exporter v2.1.0
1. Element picker now works INSIDE the maximized Deep Research overlay
The root cause was that ChatGPT's React synthetic event system calls stopPropagation() on pointer events within the overlay, preventing pointermove from reaching document-level listeners.
Fix: Replaced pointermove with native mousemove + document.elementFromPoint(x, y). This bypasses React's event interception entirely -- elementFromPoint queries the rendering tree directly regardless of what React does with events. The tip overlay is temporarily hidden during the query so it does not intercept the hit test. Combined with mousedown (instead of click) with stopImmediatePropagation(), this ensures the export fires before React's handlers can mask the event.
You can now hover over individual paragraphs, headings, list items, and citations inside the maximized deep research panel, and click to export just that element.
2. Deep Research panel extractors (Citations, Sources Scanned, Thinking Activity)
Three new dedicated extractors parse the same-origin DOM panels directly:
extractDRCitations() -- Parses section[aria-labelledby="report-references-citations"], extracts each numbered citation with title and URL into the format 1. [title](url)
extractDRScanned() -- Parses section[aria-labelledby="report-references-sources-scanned"], extracts grouped sources with snippets
extractDRActivity() -- Parses section[aria-labelledby="report-activity-title"], extracts thinking steps with titles, summaries, and "Searching" entries with their source links
3. Toggleable export options in the modal toolbar
The preview modal now has checkboxes for Citations, Scanned, and Activity that persist via GM_setValue. When pressing R or using the menu command, the export automatically appends whichever panels are toggled on.