6.4 KiB
ChatGPT Conversation DOM Specification
Application Shell
Purpose
Contains navigation, sidebar, overlays and application chrome.
Relevant Entry Point
main
Ignored
- dialogs
- scripts
- aria-live
- overlays
Conversation turn structure
Turn container
Observed selector:
section[data-turn]
Observed user-turn example:
<section
data-turn-id="837e77b6-5763-4f50-887b-573586d0fcda"
data-turn-id-container="837e77b6-5763-4f50-887b-573586d0fcda"
data-testid="conversation-turn-1"
data-turn="user">
Purpose:
- Represents one ordered conversation turn.
- Provides the strongest observed semantic boundary for a turn.
- Groups the authored message with turn-level interface elements and any auxiliary content.
- Carries turn identity, order, and role information.
Observed semantic attributes:
data-turndata-turn-iddata-turn-id-containerdata-testid
The section also carries layout and scrolling classes, but those classes appear incidental to presentation and should not be treated as stable extraction selectors.
The extractor should enumerate section[data-turn] elements in DOM order.
Message container
Observed selector:
[data-message-author-role]
Observed user-message example:
<div
data-message-author-role="user"
data-message-id="837e77b6-5763-4f50-887b-573586d0fcda">
Purpose:
- Represents the authored message region inside a conversation turn.
- Provides the author role independently of the outer turn container.
- Contains the rendered message payload, but does not necessarily contain all turn-level content.
Observed semantic attributes:
data-message-author-roledata-message-id
Relationship to the turn container:
- The message container is a descendant of
section[data-turn]. - In the observed user turn,
data-turn-id,data-turn-id-container, anddata-message-idcontain the same UUID. - This correspondence is observed in the sample but should not yet be assumed to hold for every turn type without further verification.
Rendered turn-content region
Observed selector:
[data-conversation-screenshot-content]
Observed relationship:
section[data-turn]
[data-conversation-screenshot-content]
[data-message-author-role]
turn-level actions and controls
The attribute name suggests that ChatGPT may use this node to define the content included in a conversation screenshot or similar rendered representation.
Current assessment:
- Observed: yes
- Potentially useful: yes
- Stability: unknown
- Required by extractor: not yet established
Turn-level controls
The user-turn action controls are descendants of the turn section but siblings of the rendered message-content region:
<div aria-label="Your message actions" role="group">
These controls are not conversation content and should be ignored by the extractor.
Their location confirms that the outer section[data-turn] represents the complete rendered turn, while [data-message-author-role] represents the authored-message component within that turn.
Current extraction model
section[data-turn]
Turn boundary, order, identity, and role
[data-conversation-screenshot-content]
Candidate rendered-content boundary
[data-message-author-role]
Authored message region
Turn-level controls and auxiliary UI
Ignore unless later analysis identifies meaningful content
Recommended initial traversal:
1. Select all section[data-turn] elements in DOM order.
2. Read the turn role and identity from the section attributes.
3. Locate the descendant [data-message-author-role] element.
4. Extract message content from within that semantic message region.
5. Ignore action controls and other interface-only descendants.
6. Preserve the outer turn as the unit of extraction so future sibling content can be evaluated.
Conversation materialization evidence
Scroll container
The saved full-page HTML places main > #thread inside an ancestor carrying:
[data-scroll-root]
That element also carries the vertical scrolling behavior and scroll-state attributes. It is the strongest semantic selector for the conversation viewport. The sidebar has a separate scrolling region and must not be used for conversation materialization.
Recommended lookup:
#thread
closest ancestor [data-scroll-root]
Partial rendering evidence
The saved Jellyfin snapshot contains 11 section[data-turn] elements, while the conversation is known to contain approximately 28 user turns and their corresponding assistant turns. The observed data-testid="conversation-turn-N" values contain large sequence gaps. This supports the conclusion that the saved DOM is incomplete.
Dynamic behavior requiring live verification
A static snapshot cannot establish:
- whether upward scrolling inserts older turns,
- whether newer turns are removed during traversal,
- whether downward scrolling restores newer turns,
- whether all visited turns remain in the DOM,
- or which loading indicator, if any, is shown while older turns materialize.
The capture implementation must therefore retain each observed turn by data-turn-id as it traverses, rather than assuming the final DOM contains the complete conversation. These dynamic behaviors must be confirmed against a live long conversation when a browser session is available.
Ordering evidence
Runtime observation confirms that data-testid="conversation-turn-N" values are reused within different virtualized windows. They are local presentation indices and must not be used as global conversation order.
Global ordering must instead be reconstructed from:
- stable identity from
data-turn-id, - DOM order inside each rendered window,
- and contiguous stable-ID overlap between consecutive windows.
UUID turn IDs identify turns but do not encode order and must never be sorted lexically.
Confirmed virtualization behavior
Manual runtime observation established that:
- different scroll positions expose different subsets of the conversation,
- observed windows contained 12, 11, and 9 user turns,
- turns leave the DOM as other turns enter it,
- the complete conversation does not coexist in the DOM,
- and the Jellyfin conversation contains 28 user turns with corresponding assistant turns.
Capture must therefore extract each stable turn immediately and retain ordered window observations. The final live DOM is not a complete source artifact.