Skip to content

Commit b964faa

Browse files
committed
fix(webui): More fixes [copilot]
1 parent ab29128 commit b964faa

1 file changed

Lines changed: 35 additions & 47 deletions

File tree

packages/webui/src/client/lib/VirtualElement.tsx

Lines changed: 35 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ export class ElementObserverManager {
423423
private resizeObserver: ResizeObserver
424424
private mutationObserver: MutationObserver
425425
private observedElements: Map<HTMLElement, () => void>
426-
private pendingReconnectFrame: number | undefined
426+
private isMutationObserverActive = false
427427

428428
private pruneDetachedObservedElements(): void {
429429
for (const observedElement of Array.from(this.observedElements.keys())) {
@@ -448,34 +448,59 @@ export class ElementObserverManager {
448448
})
449449
})
450450

451-
// Configure MutationObserver
451+
// Configure MutationObserver once and only connect/disconnect based on active observed elements.
452452
this.mutationObserver = new MutationObserver((mutations) => {
453+
if (this.observedElements.size === 0) return
454+
453455
this.pruneDetachedObservedElements()
454456
const targets = new Set<HTMLElement>()
455457

456458
mutations.forEach((mutation) => {
457-
const target = mutation.target as HTMLElement
458-
if (!document.contains(target)) return
459-
// Find the closest observed element
460-
let element = target
459+
let element: HTMLElement | null = null
460+
if (mutation.target instanceof HTMLElement) {
461+
element = mutation.target
462+
} else {
463+
element = mutation.target.parentElement
464+
}
465+
466+
if (!element || !document.contains(element)) return
467+
461468
while (element) {
462469
if (this.observedElements.has(element)) {
463470
targets.add(element)
464471
break
465472
}
466-
if (!element.parentElement) break
467473
element = element.parentElement
468474
}
469475
})
470476

471-
// Call callbacks for affected elements
472477
targets.forEach((element) => {
473478
const callback = this.observedElements.get(element)
474479
if (callback) callback()
475480
})
476481
})
477482
}
478483

484+
private ensureMutationObserverConnected(): void {
485+
if (this.isMutationObserverActive) return
486+
if (this.observedElements.size === 0) return
487+
if (!document.body) return
488+
489+
this.mutationObserver.observe(document.body, {
490+
childList: true,
491+
subtree: true,
492+
attributes: true,
493+
characterData: true,
494+
})
495+
this.isMutationObserverActive = true
496+
}
497+
498+
private disconnectMutationObserver(): void {
499+
if (!this.isMutationObserverActive) return
500+
this.mutationObserver.disconnect()
501+
this.isMutationObserverActive = false
502+
}
503+
479504
public static getInstance(): ElementObserverManager {
480505
if (!ElementObserverManager.instance) {
481506
ElementObserverManager.instance = new ElementObserverManager()
@@ -490,55 +515,18 @@ export class ElementObserverManager {
490515

491516
this.observedElements.set(element, callback)
492517
this.resizeObserver.observe(element)
493-
if (!this.pendingReconnectFrame) {
494-
this.mutationObserver.observe(element, {
495-
childList: true,
496-
subtree: true,
497-
attributes: true,
498-
characterData: true,
499-
})
500-
}
518+
this.ensureMutationObserverConnected()
501519
}
502520

503521
public unobserve(element: HTMLElement): void {
504522
if (!element) return
505523
this.observedElements.delete(element)
506524
this.resizeObserver.unobserve(element)
507525
this.pruneDetachedObservedElements()
508-
this.mutationObserver.disconnect()
509526

510527
if (this.observedElements.size === 0) {
511-
if (this.pendingReconnectFrame) {
512-
window.cancelAnimationFrame(this.pendingReconnectFrame)
513-
this.pendingReconnectFrame = undefined
514-
}
515-
this.mutationObserver.disconnect()
516528
this.resizeObserver.disconnect()
517-
return
518-
}
519-
520-
if (!this.pendingReconnectFrame) {
521-
this.pendingReconnectFrame = window.requestAnimationFrame(() => {
522-
this.pendingReconnectFrame = undefined
523-
524-
// MutationObserver has no per-element unobserve, so we reconnect once per frame.
525-
this.pruneDetachedObservedElements()
526-
527-
if (this.observedElements.size === 0) {
528-
this.resizeObserver.disconnect()
529-
return
530-
}
531-
532-
this.observedElements.forEach((_, el) => {
533-
if (!document.contains(el)) return
534-
this.mutationObserver.observe(el, {
535-
childList: true,
536-
subtree: true,
537-
attributes: true,
538-
characterData: true,
539-
})
540-
})
541-
})
529+
this.disconnectMutationObserver()
542530
}
543531
}
544532
}

0 commit comments

Comments
 (0)