Skip to content

Commit e44dca8

Browse files
committed
feat(browser): mark snapshot elements that appeared since the previous snapshot
After an action the agent saw a fresh snapshot but no hint of what changed, so a popup, suggestion list, or validation message looked like any other line. The page now remembers which elements an earlier snapshot of the document listed, and later snapshots mark the rest new (the first snapshot marks nothing).
1 parent d2d230d commit e44dca8

2 files changed

Lines changed: 30 additions & 1 deletion

File tree

‎apps/desktop/src/main/browser-agent/page-functions.test.ts‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ beforeEach(() => {
115115
for (const state of window.__simAgentMutationStates ?? []) state.observer.disconnect()
116116
window.__simAgentMutationStates = undefined
117117
window.__simAgentNextElementId = 0
118+
window.__simAgentShownElements = undefined
118119
window.__simAgentResolveElement = undefined
119120
installDomShims()
120121
Reflect.deleteProperty(document, 'activeElement')
@@ -748,6 +749,23 @@ describe('collectSnapshot', () => {
748749
expect(outline).not.toContain('button "x" [ref=999]')
749750
})
750751

752+
it('marks elements that appeared since the previous snapshot as new', () => {
753+
document.body.innerHTML = '<button>Compose</button>'
754+
visible(document.querySelector('button') as HTMLButtonElement)
755+
expect(outlineOf(collectSnapshot())).not.toContain(' new')
756+
757+
const dialog = document.createElement('div')
758+
dialog.innerHTML = '<input aria-label="Recipients"><button>Send</button>'
759+
document.body.append(dialog)
760+
dialog.querySelectorAll('*').forEach((el) => visible(el as HTMLElement))
761+
const lines = outlineOf(collectSnapshot()).split('\n')
762+
763+
expect(lines.find((line) => line.includes('"Compose"'))).not.toMatch(/ new$/)
764+
expect(lines.find((line) => line.includes('"Recipients"'))).toMatch(/ new$/)
765+
expect(lines.find((line) => line.includes('"Send"'))).toMatch(/ new$/)
766+
expect(outlineOf(collectSnapshot())).not.toContain(' new')
767+
})
768+
751769
it('sanitizes a malicious role so it cannot forge a second snapshot line', () => {
752770
document.body.innerHTML = '<div tabindex="0" aria-label="Safe control"></div>'
753771
const control = visible(document.querySelector('div') as HTMLDivElement)

‎apps/desktop/src/main/browser-agent/page-functions.ts‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ declare global {
4141
observedRoots: WeakSet<ParentNode>
4242
}>
4343
__simAgentNextElementId?: number
44+
/** Elements an earlier snapshot of this document listed; a later snapshot marks the rest `new`. */
45+
__simAgentShownElements?: WeakSet<Element>
4446
/** Why the last __simAgentResolveElement call returned null — read by the
4547
* shared stale-error producers so a refusal names its cause instead of
4648
* the blanket "the page changed". Cleared on every successful resolve. */
@@ -160,6 +162,11 @@ export function collectSnapshot(startingElementId = 0, elementId?: number): unkn
160162
context: string
161163
}> = []
162164
window.__simAgentElements = registry
165+
const previouslyShown = window.__simAgentShownElements
166+
const shown = previouslyShown ?? new WeakSet<Element>()
167+
window.__simAgentShownElements = shown
168+
/** Whether an element appeared since the previous snapshot; the first snapshot marks nothing. */
169+
const isNew = (el: Element): boolean => previouslyShown !== undefined && !shown.has(el)
163170
const lines: string[] = []
164171
let truncated = false
165172
let refCount = 0
@@ -553,6 +560,8 @@ export function collectSnapshot(startingElementId = 0, elementId?: number): unkn
553560
parts.push(`${attribute}=${value}`)
554561
}
555562
}
563+
if (isNew(el)) parts.push('new')
564+
shown.add(el)
556565
const suffix = parts.length > 0 ? ` ${parts.join(' ')}` : ''
557566
const lineIndex = lines.length
558567
if (push(`${indent}- ${role} ${quote(name)} [ref=${id}]${suffix}`)) {
@@ -574,8 +583,10 @@ export function collectSnapshot(startingElementId = 0, elementId?: number): unkn
574583
if (!text) return
575584
const id = registerElement(el, roleFor(el), text)
576585
textLineCount++
586+
const marker = isNew(el) ? ' new' : ''
587+
shown.add(el)
577588
const lineIndex = lines.length
578-
if (push(`${indent}- text ${quote(text)} [ref=${id}]`)) refLineIndexes[id] = lineIndex
589+
if (push(`${indent}- text ${quote(text)} [ref=${id}]${marker}`)) refLineIndexes[id] = lineIndex
579590
}
580591

581592
const headingLevel = (el: Element): number | null => {

0 commit comments

Comments
 (0)