@@ -7,10 +7,12 @@ import { Theme } from '@/components/primitives/Theme'
77
88import { InBrowserControls } from './InBrowserControls'
99
10+ let initialized = false
11+
1012function createShadowRoot ( ) {
1113 const mount = document . createElement ( 'div' )
1214
13- document . body . appendChild ( mount )
15+ document . body . prepend ( mount )
1416
1517 const shadow = mount . attachShadow ( {
1618 mode : 'open' ,
@@ -26,6 +28,14 @@ function createShadowRoot() {
2628}
2729
2830function initialize ( ) {
31+ // We have multiple points in time when we try to inject the UI. This
32+ // makes sure we actually only do it once.
33+ if ( initialized ) {
34+ return
35+ }
36+
37+ initialized = true
38+
2939 const root = createShadowRoot ( )
3040
3141 /**
@@ -69,7 +79,33 @@ function initialize() {
6979}
7080
7181if ( document . readyState === 'loading' ) {
72- window . addEventListener ( 'DOMContentLoaded' , initialize )
82+ // We use a MutationObserver to try and load the UI as soon as the body
83+ // element has been added. Otherwise we have to wait for content to be
84+ // downloaded and scripts executed, making it quite noticeable that the
85+ // UI is being injected.
86+ const mutationObserver = new MutationObserver ( ( mutations ) => {
87+ for ( const mutation of mutations ) {
88+ for ( const node of mutation . addedNodes ) {
89+ if ( node instanceof HTMLBodyElement ) {
90+ mutationObserver . disconnect ( )
91+
92+ initialize ( )
93+ break
94+ }
95+ }
96+ }
97+ } )
98+
99+ mutationObserver . observe ( document . documentElement , {
100+ childList : true ,
101+ } )
102+
103+ // Worst case scenario, we initialize the UI when the DOM is ready.
104+ window . addEventListener ( 'DOMContentLoaded' , ( ) => {
105+ mutationObserver . disconnect ( )
106+
107+ initialize ( )
108+ } )
73109} else {
74110 initialize ( )
75111}
0 commit comments