45176219a2
Core has one empty state, the intro splash, and it 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 — core has no business learning a bot's face and name. Add `chat.empty` as a contribution area, resolved by the transcript the same way `transcript.directives` resolves an inline widget. A contribution mounts for every empty session and renders nothing for the ones it does not own, so it can subscribe to its own stores and appear once they load. Pull the splash's lettering out of `intro.tsx` into a `Wordmark` primitive so a second caller cannot fork it, and move the typeface, weight and tracking into `.wordmark` beside the `.fit-text` rules they travel with — as utilities they were one class-merge or one missed scan away from silently rendering the wordmark in body text. `Wordmark` takes its width, because fit-text sizes to fill and a short name set at the splash's full width comes out enormous.
34 lines
1.4 KiB
TypeScript
34 lines
1.4 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.
|
|
*/
|
|
|
|
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
|
|
}
|