Widget UI
Render into the widget: slots, hooks, and the client API.
Beyond tool cards, an extension can render its own UI inside the widget: a composer button, a
header control, a full panel. Attach a Component and pick where it shows with useSlot.
Extension components are Solid JSX, even when your app is React. The plugin compiles conciv/extensions/* as a Solid
zone, so Show, signals, and stores all work as usual.
Slots
The widget mounts your Component once per slot and tells you where it currently is. Render
nothing for slots you do not care about.
import {Show} from 'solid-js'
import {defineExtension} from '@conciv/extension'
const deploy = defineExtension({name: 'deploy', tools: [deployRun]})
function DeployButton() {
const slot = deploy.useSlot()
const context = deploy.useContext()
return (
<Show when={slot() === 'composer'}>
<button type="button" onClick={() => context.insert('deploy this branch to staging')}>
Deploy
</button>
</Show>
)
}
export default Object.assign(deploy, {Component: DeployButton})Slots: header, footer, composer, empty (the blank-thread state), status, and widget
(a free-floating layer).
useContext
useContext() returns the host context: everything the widget gives your component. Pass a
selector to subscribe to one slice.
Composer actions, always available:
Prop
Type
Plus client (the session API client), requestMeta() (session id and model for your own
fetches), grab (the element-grab API), and currentSlot.
.client(factory)
Extension-specific client state lives in a .client(...) factory. Its return value is merged into
what useContext() returns, fully typed:
const deploy = defineExtension({name: 'deploy', tools: [deployRun]}).client(() => {
const [lastUrl, setLastUrl] = createSignal<string | null>(null)
return {value: {lastUrl, setLastUrl}}
})Return {value, dispose}; dispose runs when the widget unmounts.
The client API
useClientApi() (also exported standalone) is the widget-host surface:
Prop
Type
Mounting outside the widget
The widget host does all of the above for you. For a custom host (or a test harness) the same
machinery is exported from @conciv/extension/client:
import {mountExtension, MountedExtension} from '@conciv/extension/client'
const dispose = mountExtension(extension, {clientApi, hostContext, slot: 'widget', root})This is exactly what the testkit uses, so tested extensions run through their production mount path.