CSUI Best practices question

Can I get some advice on what is the best way for me to append a new button shown in the screenshots? The inspected the element and it doesn't seem to have a unique id i can target in the content script. My first hunch is to use querySelector but I think this is a very brittle method. So what is the best way to do this? And how would that method stand against layout changes deployed by OpenAI ?
No description
No description
11 Replies
edmund
edmundOP10mo ago
update this is what i have so far:
import type { PlasmoCSConfig, PlasmoGetInlineAnchor } from "plasmo"

export const config: PlasmoCSConfig = {
matches: ["https://chat.openai.com/*"]
}

export const getInlineAnchor: PlasmoGetInlineAnchor = async () => {
const anchor = document.querySelector('a[href="/"]')

console.log("anchor exists...?", anchor)

return document.querySelector('a[href="/"]')
}

export const TestButton = () => {
return (
<button
type="button"
className="flex items-center h-10 gap-2 px-2 font-medium rounded-lg group bg-token-surface-primary">
Test
</button>
)
}
import type { PlasmoCSConfig, PlasmoGetInlineAnchor } from "plasmo"

export const config: PlasmoCSConfig = {
matches: ["https://chat.openai.com/*"]
}

export const getInlineAnchor: PlasmoGetInlineAnchor = async () => {
const anchor = document.querySelector('a[href="/"]')

console.log("anchor exists...?", anchor)

return document.querySelector('a[href="/"]')
}

export const TestButton = () => {
return (
<button
type="button"
className="flex items-center h-10 gap-2 px-2 font-medium rounded-lg group bg-token-surface-primary">
Test
</button>
)
}
not seeing the Test button render even though it is found from document.querySelector , am i doing something wrong ?
No description
edmund
edmundOP10mo ago
update #2 i think i'm getting close... the button appears for a split second and disappears ? again, not sure what could be causing this this is what i have so far :
import type {
PlasmoCSConfig,
PlasmoCSUIJSXContainer,
PlasmoGetInlineAnchor,
PlasmoMountShadowHost,
PlasmoRender
} from "plasmo"
import { createRoot } from "react-dom/client"

export const config: PlasmoCSConfig = {
matches: ["https://chat.openai.com/*"]
}

export const getInlineAnchor: PlasmoGetInlineAnchor = async () =>
document.querySelector('a[href="/"]')

export const TestButton = () => {
return (
<button
type="button"
className="flex items-center h-10 gap-2 px-2 font-medium rounded-lg group bg-token-surface-primary">
Test
</button>
)
}

// source: https://discord.com/channels/946290204443025438/1152984889629610114/1153019220947386499
export const render: PlasmoRender<PlasmoCSUIJSXContainer> = async ({
anchor,
createRootContainer
}) => {
const rootContainer = await createRootContainer(anchor)

const root = createRoot(rootContainer)

root.render(<TestButton />)
}

// source: https://docs.plasmo.com/framework/content-scripts-ui/life-cycle#custom-dom-mounting
export const mountShadowHost: PlasmoMountShadowHost = ({
shadowHost,
anchor,
mountState
}) => {
anchor.element.appendChild(shadowHost)
mountState.observer.disconnect() // OPTIONAL DEMO: stop the observer as needed
}
import type {
PlasmoCSConfig,
PlasmoCSUIJSXContainer,
PlasmoGetInlineAnchor,
PlasmoMountShadowHost,
PlasmoRender
} from "plasmo"
import { createRoot } from "react-dom/client"

export const config: PlasmoCSConfig = {
matches: ["https://chat.openai.com/*"]
}

export const getInlineAnchor: PlasmoGetInlineAnchor = async () =>
document.querySelector('a[href="/"]')

export const TestButton = () => {
return (
<button
type="button"
className="flex items-center h-10 gap-2 px-2 font-medium rounded-lg group bg-token-surface-primary">
Test
</button>
)
}

// source: https://discord.com/channels/946290204443025438/1152984889629610114/1153019220947386499
export const render: PlasmoRender<PlasmoCSUIJSXContainer> = async ({
anchor,
createRootContainer
}) => {
const rootContainer = await createRootContainer(anchor)

const root = createRoot(rootContainer)

root.render(<TestButton />)
}

// source: https://docs.plasmo.com/framework/content-scripts-ui/life-cycle#custom-dom-mounting
export const mountShadowHost: PlasmoMountShadowHost = ({
shadowHost,
anchor,
mountState
}) => {
anchor.element.appendChild(shadowHost)
mountState.observer.disconnect() // OPTIONAL DEMO: stop the observer as needed
}
edmund
edmundOP10mo ago
@louis 🙏🏻🙏🏻🙏🏻 is this something to do with the mutation observer of the web page? it's weird how it's there and then disappears
Sean Ward
Sean Ward10mo ago
I would think that it should be working. Maybe try setting min height/width on the button you're injecting?
DunkinDeez
DunkinDeez10mo ago
I think you need to export default TestButton
edmund
edmundOP10mo ago
no luck 😦 no luck 😦 hey @louis could i get some assistance please? 🙏
lab
lab10mo ago
You're pretty much on your own with the render API - I just expose it for advanced escape hatch but otherwise it's wild west. Note that when using the render API, the anchor might not be hooked inside, so I would debug the container and see what's going on. Just log it out and see what you find.
edmund
edmundOP10mo ago
it looks like i don't need to use the render API :
import type {
PlasmoCSConfig,
PlasmoGetInlineAnchor,
PlasmoMountShadowHost,
} from "plasmo"

export const config: PlasmoCSConfig = {
matches: ["https://chat.openai.com/*"]
}

export const getInlineAnchor: PlasmoGetInlineAnchor = async () =>
document.querySelector('a[href="/"]')

export const getShadowHostId = () => "plasmo-shadow-container"

export const TestButton = () => {
return (
<button
type="button"
className="flex items-center h-20 gap-2 p-4 font-medium rounded-lg group bg-token-surface-primary min-h-1 min-w-1">
Test
</button>
)
}

// source: https://docs.plasmo.com/framework/content-scripts-ui/life-cycle#custom-dom-mounting
export const mountShadowHost: PlasmoMountShadowHost = ({
shadowHost,
anchor,
mountState
}) => {
anchor.element.appendChild(shadowHost)
}

export default TestButton
import type {
PlasmoCSConfig,
PlasmoGetInlineAnchor,
PlasmoMountShadowHost,
} from "plasmo"

export const config: PlasmoCSConfig = {
matches: ["https://chat.openai.com/*"]
}

export const getInlineAnchor: PlasmoGetInlineAnchor = async () =>
document.querySelector('a[href="/"]')

export const getShadowHostId = () => "plasmo-shadow-container"

export const TestButton = () => {
return (
<button
type="button"
className="flex items-center h-20 gap-2 p-4 font-medium rounded-lg group bg-token-surface-primary min-h-1 min-w-1">
Test
</button>
)
}

// source: https://docs.plasmo.com/framework/content-scripts-ui/life-cycle#custom-dom-mounting
export const mountShadowHost: PlasmoMountShadowHost = ({
shadowHost,
anchor,
mountState
}) => {
anchor.element.appendChild(shadowHost)
}

export default TestButton
but this still only displays the button for a split second before disappearing (attached video) - this is what i am mainly confused about, idk what could be causing this behaviour
edmund
edmundOP10mo ago
and how would i debug the container? do you mean just inspect element?
DunkinDeez
DunkinDeez10mo ago
it looks like that element is being rerendered or something so I would think you'd need to remount the button not 100% sure thats just a guess
edmund
edmundOP10mo ago
dumb question, but how would i remount as you suggested?
Want results from more Discord servers?
Add your server