832ec82f75
Ownership of an empty transcript is per session and is not known until each plugin has loaded its own data — which is why the slot mounts contributors rather than resolving a claim up front. Taking only contributions[0] then let a plugin that DECLINES a session suppress the one that owns it: silently, permanently, and only for the users whose installed plugins happen to register in that order. Every registration is mounted and answers for itself. Declining is free; two claiming one session render both, a visible conflict instead of a silent drop.
39 lines
1.7 KiB
TypeScript
39 lines
1.7 KiB
TypeScript
import type { ReactNode } from 'react'
|
|
|
|
/**
|
|
* CHAT EMPTY STATE — the blank transcript as a contribution area.
|
|
*
|
|
* Core owns exactly one empty state: the intro splash, which belongs to a
|
|
* fresh draft with no session selected. A session that exists but has nothing
|
|
* in it yet falls outside that — and whoever opened it is the only one who
|
|
* knows what should stand in the gap. A bot's chat wants the bot's face and
|
|
* name; core has neither, and should not learn them.
|
|
*
|
|
* So the slot is contributed. A plugin claims the sessions it owns by
|
|
* returning an element for them and `null` for everything else, which is also
|
|
* how it stands down while the transcript is still hydrating.
|
|
*
|
|
* Ownership is per session, so EVERY registration is mounted and each answers
|
|
* for itself. Declining is free; two plugins claiming one session render both,
|
|
* which is the visible cost of never letting registration order silently
|
|
* suppress the plugin that actually owns the chat.
|
|
*/
|
|
|
|
export const CHAT_EMPTY_AREA = 'chat.empty'
|
|
|
|
/** Props handed to a chat-empty contribution's `render`. */
|
|
export interface ChatEmptyProps {
|
|
/** The live session whose transcript is empty. */
|
|
sessionId: string
|
|
}
|
|
|
|
/** Payload of a `chat.empty` contribution's `data`. */
|
|
export interface ChatEmptyContribution {
|
|
/** Renders the empty state, or returns `null` to decline the session and
|
|
* leave the transcript blank. Mounted as a component inside the
|
|
* contribution error boundary, so it can subscribe to its own stores and
|
|
* appear once they load — the roster a bot chat needs arrives after the
|
|
* transcript does. A throw degrades to an inline error, not a dead chat. */
|
|
render: (props: ChatEmptyProps) => ReactNode
|
|
}
|