/** * EmbeddableChat — lib-portable port of the hub's `` component. * * Drops every hub-only import (auth-provider, useNavLink, currentPlatform, * tableIdForDocumentType, rag-table-config, etc.) and routes ALL navigation * + identity decisions through `useRequiredChatRuntime()`. Host wires the * runtime once at root (HubRuntimeProvider in the hub, custom provider in * embedders); this component reads from it everywhere. * * Diff summary vs hub original: * - `useAuth()` → `useRequiredChatRuntime().user` (greeting + identity only; * the requireAuth render gate is dropped — hub's wrapper handles it). * - `currentPlatform()` → `useRequiredChatRuntime().source`. * - `useNavLink`/`NavLinkAnchor` → chip-anchor rewrite via * `handleChatNavClick` + lib's `NavLinkAnchorViaRuntime`. * - `useDocChat(source)` → `useSseChatAdapter()` (reads source from runtime). * - `tableIdForDocumentType` import deleted (dead per audit). * - `renderChatInlineEntityCard` imported from lib's entity-cards barrel. * - `useCloseOnNavigation` signature is `(close, pathname)` — pass `null` * because lib has no `usePathname()` and embedders own that decision. * - All other hub utilities (chat-attachment-bar, slash-commands fetcher, * icon registry, chip-styles, click-utils, etc.) re-resolved against * the equivalent lib modules. * * Public surface — `` taking the same prop bundle the hub * shell passes (minus `requireAuth`, which the hub's wrapper handles), plus * an optional `extras` opt-in for the chat-card dispatch helpers that need * host-supplied builders (program configs, product-release prop builder). */ import React from 'react'; import { type MingoWelcomeProps } from './mingo-welcome'; import { type GuideWelcomeProps } from './guide-welcome'; import type { ChatCardDispatchExtras } from './entity-cards/dispatch'; import { type ChatMode, type UseUnifiedChatModes } from './hooks/use-unified-chat'; import type { DialogItem } from './types/component.types'; import type { UnifiedChatState } from './types/unified-chat-state.types'; import type { MessageSegment } from './types/message.types'; import type { FetchDialogsParams, FetchDialogsResult } from './hooks/use-nats-chat-adapter'; import type { ChatContextItem, ChatContextPickerConfig } from './types/context-item.types'; export interface EmbeddableChatProps { /** Base route for in-app doc chip nav (e.g. `/knowledge-base`). When * omitted, defaults are derived from `runtime.source`. */ baseRoute?: string; /** When the embedder doesn't host a `[...path]` route to render markdown * chips against, set this to a platform that does. Chips with * `externalUrl: null` resolve to `getBaseUrl(chipBasePlatform) + * '/knowledge-base/' + path` and open in a new tab. */ chipBasePlatform?: string; /** DB-driven list of enabled RAG table ids (chip catalog filter). * Prefetched server-side by the embedder's wrapper. Empty/null → no * empty-state chips render. */ enabledRagTableIds?: ReadonlyArray | null; /** DB-backed empty-state greeting. Falls back to a generic greeting. */ emptyStateGreeting?: string | null; /** DB-backed starter prompts (chips below the greeting). */ suggestedQueries?: ReadonlyArray | null; /** Controlled-mode open state. When provided, `onOpenChange` MUST also * be provided. Uncontrolled mode is the default. */ open?: boolean; /** Controlled-mode change handler. Required when `open` is provided. */ onOpenChange?: (open: boolean) => void; /** Initial open state for uncontrolled mode. Ignored if `open` is set. */ defaultOpen?: boolean; /** Render the built-in floating "Ask AI" trigger. Defaults to `true`. */ showInternalTrigger?: boolean; /** * Non-interactive display mode. When `true`, the whole panel becomes a * static visual — every hover state and click is blocked (via * `pointer-events-none` on the panel body) and the composer no longer * auto-focuses. Use it to embed the chat as a marketing/hero mock (e.g. a * scripted `mingoState` thread) that should look live but not respond to the * cursor. Scrolling is intentionally disabled too. Defaults to `false`. */ previewMode?: boolean; /** Optional builders for chat-card types whose props live in hub-land * (programs + product_release). Forwarded straight to * `renderChatInlineEntityCard`. */ extras?: ChatCardDispatchExtras; /** Optional callback used by `useSseChatAdapter`'s `displayRef` / * `discussRef` flow to translate an LLM document type into the * registry table id for entity-id-filtered retrieval. * * Legacy top-level form. The new shape is `modes.guide.tableIdForDocumentType`. * When both are present, `modes` wins. */ tableIdForDocumentType?: (documentType: string) => string | null; /** * Per-mode transport configuration. When omitted, the component * falls back to legacy guide-only behaviour synthesised from the * top-level `tableIdForDocumentType` prop — multi-platform-hub and * any other existing consumer keep working with zero changes. * * When provided, this is the canonical way to wire chat transports: * * - `modes.guide` → SSE/Guide adapter options (RAG retrieval, hub). * - `modes.mingo` → NATS/Mingo adapter config (agent, openframe). * * Configuring both modes makes the in-panel mode toggle appear so * the user can flip between Guide and Mingo without losing either * history (each mode keeps its own local thread). */ modes?: UseUnifiedChatModes; /** * Pre-built Mingo-mode state, supplied by the host instead of letting the * built-in NATS adapter own it. When provided, the panel renders Mingo mode * from this object and opens no subscription of its own — the host keeps * chat data + streaming in its own store/cache so it survives the panel * unmounting (no `keepMounted` needed). The host should then NOT pass * `modes.mingo` (Guide-mode wiring via `modes.guide` is unaffected). */ mingoState?: UnifiedChatState; /** * Approval cards to pin as the sticky footer under the thread. * * Hosts that lift PENDING approvals out of the message list (mingo does: * a pending card is filtered out of its bubble so an interrupted retry cannot * render the same request twice) must hand them back here — otherwise the * card exists in the reducer, is stripped on the way to the view, and is * displayed nowhere at all. `ChatMessageList` renders them with the same * component the inline path uses, so the approve/reject handlers stamped on * the segments keep working. */ pendingApprovals?: MessageSegment[]; /** * Dialog-management capabilities for injected Mingo mode (`mingoState`). * * When the host injects `mingoState`, it doesn't pass `modes.mingo` (that * would re-activate the idle built-in adapter), so the rename/archive/ * restore/archive-page affordances can't read their capability flags off the * callback config. Supply them here instead. `canRename`/`canArchive` default * to `true` when `mingoState` is set; the archive page + restore are shown * only when their callbacks are provided. Ignored unless `mingoState` is set. */ mingoDialogCapabilities?: { canRename?: boolean; canArchive?: boolean; fetchArchivedDialogs?: (params: FetchDialogsParams) => Promise; unarchiveDialog?: (id: string) => Promise; searchQuery?: string; onSearchChange?: (query: string) => void; /** Copy a shareable link to a conversation — adds "Copy chat link" to the * header ⋯ menu and every dialog row menu. The host owns the URL shape and * the clipboard write; the panel knows neither the app's routes nor whether * a clipboard is available. Omit to hide the action. */ onCopyLink?: (dialog: DialogItem) => void; }; /** * Controlled active-mode. When provided, `onActiveModeChange` MUST * also be provided. For uncontrolled use see `defaultActiveMode`. */ activeMode?: ChatMode; /** Controlled active-mode change handler. Required when `activeMode` is set. */ onActiveModeChange?: (mode: ChatMode) => void; /** * OpenFrame AI agent mode — render a global agent (e.g. `'fae'`, `'mingo'`). * Works in BOTH regular (host) AND embedded modes. When set, the chat fetches * that agent's display config (greeting + suggested prompts) instead of the * platform empty-state — the "agent mode" URL override. DISPLAY-only this * phase: retrieval still resolves server-side from the platform. Optional; * unset = today's behavior. * * Route resolution (highest → lowest precedence): * 1. `aiAgentConfigUrl` prop (below) * 2. `runtime.endpoints.aiAgentConfigUrl` * 3. the component's built-in default (`/api/ai-agents/`) * So agent mode works with ZERO wiring, yet every route stays overridable. */ activeAgentSlug?: string; /** Optional agent switcher callback (host renders the agent picker UI). */ onAgentChange?: (slug: string) => void; /** * Per-component override for the agent display-config route. Highest * precedence (over `runtime.endpoints.aiAgentConfigUrl` and the built-in * default). Lets a single embed point a specific agent at a custom/proxied * endpoint without changing the shared runtime. Optional. */ aiAgentConfigUrl?: (slug: string) => string; /** * Initial active mode for uncontrolled mode. Ignored when `activeMode` * is set. Defaults to `'guide'` when `modes.guide` is configured, * else `'mingo'`. */ defaultActiveMode?: ChatMode; /** * Wrapper shell around the chat body. * - `'drawer'` (default): wraps in a body-level Radix Drawer (slide-in * overlay from the right) — the original MPH / standalone behaviour. * - `'none'`: no shell. Renders only the chat body so the consumer can * host it inside their own container (e.g. `AppLayoutDrawerContent`). * The internal "Ask AI" trigger and iOS body scroll-lock are also * suppressed — those are Drawer-shell concerns. The consumer is * responsible for mount/unmount and for opening/closing via the * `open` / `onOpenChange` props (which the in-body close button still * drives). */ shell?: 'drawer' | 'none'; /** * Display name of the signed-in user, shown as the sub-line under the chat * title in the panel header. The server-resolved chat identity * (`useChatIdentity().user.name`) always wins when present; this is the * host-supplied fallback so the header still shows who's signed in when the * identity endpoint doesn't return a name (e.g. a tenant whose identity * route omits it). Empty/undefined → the header renders the title alone. */ userDisplayName?: string; /** * Avatar URL of the signed-in user — the header-avatar counterpart of * `userDisplayName`. Used for the New Chat compose view (and as the * fallback when an open conversation's dialog carries no owner info). * The server-resolved identity avatar wins when present; absent both → * initials derived from the display name. */ userAvatarUrl?: string; /** * Content overrides for the default (Mingo-mode) empty state * (``): greeting `title`/`subtitle`, the `promo` card, and * extra `quickActions` chips. * Each field falls back to the built-in OpenFrame defaults, so the kit * stays platform-agnostic. `onStartGuideChat` and `hasExistingChats` are * wired internally and are NOT overridable here. The quick-action * hover-preview callbacks are also wired internally. */ mingoWelcome?: Omit; /** * Content overrides for the Guide-mode empty state (``): * greeting `title`/`subtitle` and the `quickActions` chips. Each field falls * back to the built-in OpenFrame defaults. `onQuickAction` and the * slash-command list `children` are wired internally and not overridable. */ guideWelcome?: Omit; /** * Entity-context picker config (Figma 31:28708 / 1:5699). When provided, the * composer renders the `+` "Assign Item" menu, the `@`-mention trigger, the * two-level picker (entity-type list → searchable multi-select), and the * selected-item chips; the selection rides out on send via * `sendMessage(text, { contextItems })`, which the host folds into its * outgoing payload. The host owns every entity source (REST/GraphQL) behind * `config.search`. Omit to disable the feature entirely. */ contextPicker?: ChatContextPickerConfig; /** * Host renderer for inline AI mentions `@marker:id` (e.g. the assistant * echoing `@device:` in its reply). DIRECT MIRROR of * `renderEntityCard` for the `[card://]` grammar: the lib detects the token, * parses `{marker, id}`, and renders whatever the host returns — typically a * SELF-FETCHING chip (each entity type has its own fetcher) that resolves its * own display name by id. SEPARATE from `contextPicker`/`contextItems` (the * USER's attachments). Keep the function identity stable (module const / * `useCallback`) so the thread's streaming memo holds. Return null for a * marker the host can't render → the lib falls back to the bare token. */ renderMention?: (reference: { marker: string; id: string; }) => React.ReactNode; /** * Host renderer that REPLACES the default label-only context chip on a sent * user bubble with a self-fetching entity chip — so a user's manually * attached context (`contextItems`) renders IDENTICALLY to an inline * `@marker:id` mention (same live name resolution + link). Mirror of * `renderMention`, for the attached-chip strip instead of inline tokens. * Return null for an item the host can't render → the lib falls back to the * label pill. Keep the identity stable (module const / `useCallback`). */ renderContextItem?: (item: ChatContextItem) => React.ReactNode; /** * One-shot prompt auto-sent into GUIDE mode. When set to a non-empty string * while `activeMode === 'guide'`, the panel sends it once via the active * (Guide/SSE) transport on the next render — e.g. an "Ask Mingo about X" * empty-state launcher that wants contextual guidance about a section — and * then invokes `onGuidePromptConsumed` so the host can null it. Nulling the * prop re-arms the one-shot, so the SAME text can be launched again later. * No-op in Mingo mode (the host should force `activeMode='guide'` alongside). */ guidePendingPrompt?: string | null; /** * Called once `guidePendingPrompt` has been handed to the Guide transport, so * the host can clear its queued prompt (which re-arms the one-shot). */ onGuidePromptConsumed?: () => void; /** * CONTEXT MEMORY (Figma 271:38656) — the entities the host collected from the * user's navigation history and rides out on every message. Rendered as a * summary strip at the top of the composer card ("N recently viewed items in * context") with a `⋯` dropdown listing them all, each removable via * `onRemove`. Replaces the old under-the-header "current page context" banner. * * Host-owned data (it lives in the host's navigation store); the lib only * places and styles it, resolving lead glyphs from `contextPicker.entityTypes`. * Shown only alongside an active `contextPicker` (i.e. Mingo mode); an empty * `items` array renders nothing. Keep the object identity stable (`useMemo`). */ contextMemory?: { items: ChatContextItem[]; onRemove?: (item: ChatContextItem) => void; }; } /** * EmbeddableChat — the floating "Ask AI" button + Mingo chat panel. * Lib-portable port of the hub's ``. */ /** * Imperative escape hatch for the ONE thing a host can't express as a prop: * "put the panel on a new chat, now". Everything else the host drives is state * it already owns (open, active dialog, mode); this is a command, and it has to * work whether the panel was just mounted or has been open on the chat list for * a while — which a mount-time prop or a `view` prop can't do (re-asserting the * same value is not a change). */ export interface EmbeddableChatHandle { /** * Same as the panel's own "Start New Chat": clears the open conversation and * its messages, force-closes the archive, and — in the narrow single-column * layout — navigates from the "Current Chats" list to the composer. Wide * layouts already show the new-chat welcome once nothing is open, so there * the compose flag is inert. */ startNewChat: () => void; } export declare const EmbeddableChat: React.ForwardRefExoticComponent>; //# sourceMappingURL=embeddable-chat.d.ts.map