Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 25 additions & 14 deletions packages/app/src/shell/titlebar/right-slot.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createContext, onCleanup, onMount, Show, useContext, type ParentProps } from "solid-js"
import { onCleanup, onMount, Show, type ParentProps } from "solid-js"
import { createStore } from "solid-js/store"
import { Portal } from "solid-js/web"

Expand All @@ -14,16 +14,35 @@ type TitlebarRightSlot = {
setMount: (mount: HTMLElement) => void
}

const TitlebarRightContext = createContext<TitlebarRightSlot>()
// Module-level singleton slot. The provider and consumers all reference this
// instance, which sidesteps SolidJS context propagation issues across lazy-loaded
// route chunks where useSlot would otherwise throw "must be used within
// TitlebarRightProvider" even though the provider is mounted.
const [slotStore, setSlotStore] = createStore<{ mount?: HTMLElement; registrations: symbol[] }>({
registrations: [],
})

const slot: TitlebarRightSlot = {
mount: () => slotStore.mount,
setMount: (mount) => setSlotStore("mount", mount),
createRegistration() {
const id = Symbol()
return {
active: () => slotStore.registrations.at(-1) === id,
register: () => setSlotStore("registrations", (items) => [...items, id]),
unregister: () => setSlotStore("registrations", (items) => items.filter((item) => item !== id)),
}
},
}

export function TitlebarRightProvider(props: ParentProps) {
return (
<TitlebarRightContext.Provider value={createTitlebarRightSlot()}>{props.children}</TitlebarRightContext.Provider>
)
return <>{props.children}</>
}

export function createTitlebarRightSlot(): TitlebarRightSlot {
const [store, setStore] = createStore<{ mount?: HTMLElement; registrations: symbol[] }>({ registrations: [] })
const [store, setStore] = createStore<{ mount?: HTMLElement; registrations: symbol[] }>({
registrations: [],
})
return {
mount: () => store.mount,
setMount: (mount) => setStore("mount", mount),
Expand All @@ -39,12 +58,10 @@ export function createTitlebarRightSlot(): TitlebarRightSlot {
}

export function TitlebarRightMount() {
const slot = useTitlebarRightSlot()
return <div ref={slot.setMount} id="opencode-titlebar-right" class="flex shrink-0 items-center justify-end gap-0" />
}

export function TitlebarRight(props: ParentProps) {
const slot = useTitlebarRightSlot()
const registration = slot.createRegistration()
onMount(() => {
registration.register()
Expand All @@ -57,9 +74,3 @@ export function TitlebarRight(props: ParentProps) {
</Show>
)
}

function useTitlebarRightSlot() {
const slot = useContext(TitlebarRightContext)
if (!slot) throw new Error("TitlebarRight must be used within TitlebarRightProvider")
return slot
}
Loading