@@ -8,6 +8,8 @@ import { EError, EShadowError } from "@lib/utils/error";
88import { DevPodLogoIcon } from "@src/icons/devpod" ;
99import { StrictMode } from "react" ;
1010import { ButtonLink } from "@lib/components/Button" ;
11+ import { runtime } from "webextension-polyfill" ;
12+ import { UpdateMessage } from "@lib/utils/messages" ;
1113
1214type PortalProps = { portal : HTMLElement | DocumentFragment } ;
1315
@@ -46,7 +48,13 @@ export function Control({ portal }: PortalProps) {
4648 < StrictMode >
4749 < div className = "flex justify-center items-center" >
4850 < ErrorBoundaryProvider >
49- < ButtonLink href = { getDevPodUrl ( ) } color = "primary" { ...bindTarget } >
51+ < ButtonLink
52+ rel = "noreferrer"
53+ target = "_blank"
54+ href = { getDevPodUrl ( ) }
55+ color = "primary"
56+ { ...bindTarget }
57+ >
5058 < DevPodLogoIcon
5159 aria-label = ""
5260 className = "w-6 h-6 text-primary-contrast"
@@ -84,14 +92,11 @@ function attachShadow<E extends Element = Element>(target: E | null) {
8492 const shadowTarget = document . createElement ( "div" ) ;
8593 target . appendChild ( shadowTarget ) ;
8694 const shadow = shadowTarget . attachShadow ( { mode : "closed" } ) ;
87- const rootContainer = document . createElement ( "div" ) ;
88- shadow . appendChild ( rootContainer ) ;
8995 attachStyles ( shadow ) ;
90- return shadow ;
96+ return { shadow, target : shadowTarget } ;
9197}
9298
9399const MAX_INIT_ATTEMPTS = 12 ;
94- let isInitialized = false ;
95100
96101function findDOMNodeByContent ( content : string ) {
97102 for ( const node of document . querySelectorAll ( "button" ) ) {
@@ -107,19 +112,41 @@ function findDOMNodeByContent(content: string) {
107112 } ) ;
108113}
109114
115+ let buttonContainer : HTMLDivElement | null = null ;
116+
110117function init ( attempts : number = 0 ) {
111- if ( isInitialized || attempts > MAX_INIT_ATTEMPTS ) return ;
118+ if ( attempts > MAX_INIT_ATTEMPTS ) {
119+ // Too many attempts, aborting
120+ return ;
121+ }
122+ if ( document . contains ( buttonContainer ) ) {
123+ // ALready initialized
124+ return ;
125+ }
112126 try {
113127 const buttonTarget = findDOMNodeByContent ( "Code" ) ?. parentElement ;
114- const rootContainer = attachShadow ( buttonTarget ) ;
128+ const { shadow : rootContainer , target : rootContainerTarget } =
129+ attachShadow ( buttonTarget ) ;
130+ buttonContainer = rootContainerTarget ;
115131 const root = createRoot ( rootContainer ) ;
116- const portalContainer = attachShadow ( document . body ) ;
132+ const { shadow : portalContainer } = attachShadow ( document . body ) ;
117133 root . render ( < Control portal = { portalContainer } /> ) ;
118- isInitialized = true ;
119134 } catch ( error ) {
135+ console . debug ( "Initialization failed" , error ) ;
120136 // 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1280ms, 2560ms, 5120ms, 10240ms, 20480ms
121137 setTimeout ( ( ) => init ( attempts + 1 ) , Math . pow ( 2 , attempts ) * 10 ) ;
122138 }
123139}
124140
141+ console . debug ( "Initializing DevPod button" ) ;
125142init ( ) ;
143+ runtime . onMessage . addListener ( ( message ) => {
144+ UpdateMessage . safeParseAsync ( message ) . then ( async ( result ) => {
145+ if ( ! result . success ) return ;
146+ const message = result . data ;
147+ console . debug ( `Received message ${ message . type } ` ) ;
148+ if ( message . type === "devpod-update" ) {
149+ init ( ) ;
150+ }
151+ } ) ;
152+ } ) ;
0 commit comments