@chativa/core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +371 -0
- package/dist/index.js +380 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function O(e,t="text"){return{id:`${Date.now()}-${Math.random().toString(36).slice(2,9)}`,type:t,data:{text:e},timestamp:Date.now()}}function _(e){return e===void 0?"":typeof e=="function"?e():e}const b={colors:{primary:"#4f46e5",secondary:"#6c757d",background:"#ffffff",text:"#212529",border:"#dee2e6"},position:"bottom-right",positionMargin:"2",size:"medium",layout:{width:"360px",height:"520px",maxWidth:"100%",maxHeight:"100%",horizontalSpace:"2",verticalSpace:"2"}};function F(e){return{"--chativa-primary-color":e.colors.primary,"--chativa-secondary-color":e.colors.secondary,"--chativa-background-color":e.colors.background,"--chativa-text-color":e.colors.text,"--chativa-border-color":e.colors.border}}function w(e,t){return{...e,...t,colors:{...e.colors,...t.colors??{}},layout:{...e.layout,...t.layout??{}}}}const a=new Map,M={register(e){a.set(e.name,e)},unregister(e){a.delete(e)},get(e){return a.get(e)},list(){return Array.from(a.values())},execute(e,t){const n=a.get(e);if(!n)return!1;const r={args:t};return n.execute(r),!0},has(e){return a.has(e)},clear(){a.clear()}},l=new Map,c={beforeSend:[],afterReceive:[],onOpen:[],onClose:[]};function A(){return{onBeforeSend(e){c.beforeSend.push(e)},onAfterReceive(e){c.afterReceive.push(e)},onWidgetOpen(e){c.onOpen.push(e)},onWidgetClose(e){c.onClose.push(e)},registerCommand(e){M.register(e)}}}const S={install(e){if(l.has(e.name))throw new Error(`ExtensionRegistry: extension "${e.name}" is already installed.`);l.set(e.name,e),e.install(A())},uninstall(e){const t=l.get(e);t&&(t.uninstall?.(),l.delete(e))},runBeforeSend(e){let t=e;for(const n of c.beforeSend){if(t===null)return null;t=n(t)}return t},runAfterReceive(e){let t=e;for(const n of c.afterReceive){if(t===null)return null;t=n(t)}return t},notifyOpen(){c.onOpen.forEach(e=>e())},notifyClose(){c.onClose.forEach(e=>e())},has(e){return l.has(e)},list(){return[...l.keys()]},clear(){l.clear(),c.beforeSend=[],c.afterReceive=[],c.onOpen=[],c.onClose=[]}};let m=null;const u=new Map,p={register(e,t){u.set(e,t)},resolve(e){const t=u.get(e);if(t)return t;if(m)return m;throw new Error(`MessageTypeRegistry: no component for type "${e}" and no fallback registered.`)},setFallback(e){m=e},has(e){return u.has(e)},unregister(e){u.delete(e)},clear(){u.clear(),m=null},list(){return[...u.keys()]}},T=e=>{let t;const n=new Set,r=(d,R)=>{const y=typeof d=="function"?d(t):d;if(!Object.is(y,t)){const E=t;t=R??(typeof y!="object"||y===null)?y:Object.assign({},t,y),n.forEach(x=>x(t,E))}},o=()=>t,C={setState:r,getState:o,getInitialState:()=>v,subscribe:d=>(n.add(d),()=>n.delete(d))},v=t=e(r,o,C);return C},H=(e=>e?T(e):T),f=new Set,g=H(e=>({messages:[],addMessage:t=>e(n=>f.has(t.id)?n:(f.add(t.id),{messages:[...n.messages,t]})),prependMessages:t=>e(n=>{const r=t.filter(o=>!f.has(o.id));return r.forEach(o=>f.add(o.id)),{messages:[...r,...n.messages]}}),removeById:t=>e(n=>({messages:n.messages.filter(r=>r.id!==t)})),updateById:(t,n)=>e(r=>({messages:r.messages.map(o=>o.id===t?{...o,...n}:o)})),clear:()=>{f.clear(),e(()=>({messages:[]}))}})),s=H((e,t)=>({isOpened:!1,isRendered:!1,isFullscreen:!1,allowFullscreen:!0,activeConnector:"dummy",connectorStatus:"idle",isTyping:!1,unreadCount:0,reconnectAttempt:0,theme:b,hasMoreHistory:!1,isLoadingHistory:!1,historyCursor:void 0,toggle:()=>e(n=>({isOpened:!n.isOpened,isRendered:!0,unreadCount:0})),open:()=>e(()=>({isOpened:!0,isRendered:!0,unreadCount:0})),close:()=>e(()=>({isOpened:!1})),toggleFullscreen:()=>e(n=>({isFullscreen:!n.isFullscreen})),setFullscreen:n=>e(()=>({isFullscreen:n})),setAllowFullscreen:n=>e(()=>({allowFullscreen:n})),setTheme:n=>e(r=>({theme:w(r.theme,n)})),getTheme:()=>t().theme,setConnector:n=>e(()=>({activeConnector:n})),setConnectorStatus:n=>e(()=>({connectorStatus:n})),setTyping:n=>e(()=>({isTyping:n})),incrementUnread:()=>e(n=>({unreadCount:n.unreadCount+1})),resetUnread:()=>e(()=>({unreadCount:0})),setReconnectAttempt:n=>e(()=>({reconnectAttempt:n})),setHasMoreHistory:n=>e(()=>({hasMoreHistory:n})),setIsLoadingHistory:n=>e(()=>({isLoadingHistory:n})),setHistoryCursor:n=>e(()=>({historyCursor:n})),subscribe:n=>s.subscribe(()=>n())}));class k{constructor(t){this._reconnectTimer=null,this._destroyed=!1,this.connector=t}async init(){this._destroyed=!1,this.connector.onMessage(t=>{s.getState().setTyping(!1);const n=S.runAfterReceive(t);if(n===null)return;s.getState().isOpened||s.getState().incrementUnread();const r=p.resolve(n.type);g.getState().addMessage({...n,from:"bot",component:r})}),this.connector.onDisconnect?.(t=>{s.getState().setConnectorStatus("disconnected"),!this._destroyed&&t!=="user"&&this._scheduleReconnect(1)}),this.connector.onTyping?.(t=>{s.getState().setTyping(t)}),this.connector.onMessageStatus?.((t,n)=>{g.getState().updateById(t,{status:n})}),s.getState().setConnectorStatus("connecting");try{await this.connector.connect(),s.getState().setConnectorStatus("connected"),this.connector.loadHistory&&await this.loadHistory()}catch(t){throw s.getState().setConnectorStatus("error"),t}}_scheduleReconnect(t){if(this._destroyed||t>3){s.getState().setConnectorStatus("error"),s.getState().setReconnectAttempt(0);return}s.getState().setReconnectAttempt(t),s.getState().setConnectorStatus("connecting");const n=2e3*t;this._reconnectTimer=setTimeout(async()=>{if(!this._destroyed)try{await this.connector.connect(),s.getState().setConnectorStatus("connected"),s.getState().setReconnectAttempt(0)}catch{this._scheduleReconnect(t+1)}},n)}async sendFeedback(t,n){await this.connector.sendFeedback?.(t,n)}async send(t){const n=S.runBeforeSend(t);if(n!==null){if(this.connector.addSentToHistory!==!1){const r=p.resolve(n.type);g.getState().addMessage({...n,from:"user",component:r,status:"sending"})}await this.connector.sendMessage(n),this.connector.addSentToHistory!==!1&&g.getState().updateById(n.id,{status:"sent"})}}async sendFile(t,n){await this.connector.sendFile?.(t,n)}async loadHistory(){if(!this.connector.loadHistory)return;const{isLoadingHistory:t,historyCursor:n}=s.getState();if(!t){s.getState().setIsLoadingHistory(!0);try{const r=await this.connector.loadHistory(n),o=r.messages.map(h=>({...h,from:h.from??"bot",component:p.resolve(h.type)}));g.getState().prependMessages(o),s.getState().setHasMoreHistory(r.hasMore),s.getState().setHistoryCursor(r.cursor)}finally{s.getState().setIsLoadingHistory(!1)}}}async destroy(){this._destroyed=!0,this._reconnectTimer!==null&&(clearTimeout(this._reconnectTimer),this._reconnectTimer=null),s.getState().setReconnectAttempt(0),await this.connector.disconnect(),s.getState().setConnectorStatus("disconnected")}}const i=new Map,I={register(e){if(i.has(e.name))throw new Error(`ConnectorRegistry: connector "${e.name}" is already registered. Call ConnectorRegistry.unregister("${e.name}") first.`);i.set(e.name,e)},get(e){const t=i.get(e);if(!t)throw new Error(`ConnectorRegistry: connector "${e}" not found. Available: [${[...i.keys()].join(", ")}]`);return t},has(e){return i.has(e)},unregister(e){i.delete(e)},clear(){i.clear()},list(){return[...i.keys()]}};exports.ChatEngine=k;exports.ConnectorRegistry=I;exports.DEFAULT_THEME=b;exports.ExtensionRegistry=S;exports.MessageTypeRegistry=p;exports.SlashCommandRegistry=M;exports.chatStore=s;exports.createOutgoingMessage=O;exports.mergeTheme=w;exports.messageStore=g;exports.resolveText=_;exports.themeToCSS=F;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/domain/entities/Message.ts","../src/domain/ports/ISlashCommand.ts","../src/domain/value-objects/Theme.ts","../src/application/registries/SlashCommandRegistry.ts","../src/application/registries/ExtensionRegistry.ts","../src/application/registries/MessageTypeRegistry.ts","../../../node_modules/.pnpm/zustand@5.0.11/node_modules/zustand/esm/vanilla.mjs","../src/application/stores/MessageStore.ts","../src/application/stores/ChatStore.ts","../src/application/ChatEngine.ts","../src/application/registries/ConnectorRegistry.ts"],"sourcesContent":["/**\n * Domain entities for messages.\n * No external dependencies allowed in this file.\n */\n\nexport type MessageSender = \"user\" | \"bot\";\n\n/** Delivery/read status for a message. */\nexport type MessageStatus = \"sending\" | \"sent\" | \"read\";\n\n/** A tappable chip/button sent alongside a bot message. */\nexport interface MessageAction {\n label: string;\n /** Text to send when tapped; falls back to `label` if omitted. */\n value?: string;\n}\n\nexport interface IncomingMessage {\n id: string;\n type: string;\n from?: MessageSender;\n data: Record<string, unknown>;\n timestamp?: number;\n /** Optional quick-reply chips rendered below the message bubble. */\n actions?: MessageAction[];\n}\n\nexport interface OutgoingMessage {\n id: string;\n type: string;\n data: Record<string, unknown>;\n timestamp?: number;\n}\n\n/** Result returned by IConnector.loadHistory(). */\nexport interface HistoryResult {\n messages: IncomingMessage[];\n hasMore: boolean;\n /** Opaque cursor passed back to load the next (older) page. */\n cursor?: string;\n}\n\nexport function createOutgoingMessage(\n text: string,\n type = \"text\"\n): OutgoingMessage {\n return {\n id: `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,\n type,\n data: { text },\n timestamp: Date.now(),\n };\n}\n","/**\n * ISlashCommand — Port definition for slash commands.\n * No external dependencies allowed in this file.\n */\n\nexport interface CommandContext {\n /** Everything typed after the command name (trimmed). */\n args: string;\n}\n\nexport interface ISlashCommand {\n /** Command name without the leading slash, e.g. \"clear\". */\n readonly name: string;\n /**\n * Short description shown in the autocomplete popup.\n * Pass a function for lazy i18n evaluation at render time:\n * `description: () => t(\"commands.clear.description\")`\n */\n readonly description: string | (() => string);\n /**\n * Optional usage hint displayed next to the command name.\n * Shows accepted parameters, e.g. `\"[count]\"` or `\"<text>\"`.\n * Also supports lazy i18n: `() => t(\"commands.clear.usage\")`\n */\n readonly usage?: string | (() => string);\n execute(context: CommandContext): void;\n}\n\n/** Resolve a description or usage value, calling it if it is a function. */\nexport function resolveText(value: string | (() => string) | undefined): string {\n if (value === undefined) return \"\";\n return typeof value === \"function\" ? value() : value;\n}\n","/**\n * Theme value object — pure types, no logic.\n * No external dependencies allowed.\n */\n\nexport type ButtonPosition = \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\";\nexport type ButtonSize = \"small\" | \"medium\" | \"large\";\nexport type SpaceLevel = \"1\" | \"2\" | \"3\" | \"4\" | \"5\";\n\n/** Avatar configuration for bot and user sides. */\nexport interface AvatarConfig {\n /** URL for the bot avatar image. Omit to use the default robot SVG. */\n bot?: string;\n /** URL for the user avatar image. Omit to use the default person SVG. */\n user?: string;\n /** Show bot avatar. Default: true. */\n showBot?: boolean;\n /** Show user avatar. Default: true. */\n showUser?: boolean;\n}\n\nexport interface ThemeColors {\n primary: string;\n secondary: string;\n background: string;\n text: string;\n border: string;\n}\n\nexport interface LayoutConfig {\n width?: string;\n height?: string;\n maxWidth?: string;\n maxHeight?: string;\n horizontalSpace?: SpaceLevel;\n verticalSpace?: SpaceLevel;\n}\n\nexport interface ThemeConfig {\n colors: ThemeColors;\n position: ButtonPosition;\n positionMargin?: SpaceLevel;\n size: ButtonSize;\n layout: LayoutConfig;\n /** Avatar configuration for bot and user sides. */\n avatar?: AvatarConfig;\n /** Show delivery/read status ticks on user messages. Default: false. */\n showMessageStatus?: boolean;\n}\n\nexport const DEFAULT_THEME: ThemeConfig = {\n colors: {\n primary: \"#4f46e5\",\n secondary: \"#6c757d\",\n background: \"#ffffff\",\n text: \"#212529\",\n border: \"#dee2e6\",\n },\n position: \"bottom-right\",\n positionMargin: \"2\",\n size: \"medium\",\n layout: {\n width: \"360px\",\n height: \"520px\",\n maxWidth: \"100%\",\n maxHeight: \"100%\",\n horizontalSpace: \"2\",\n verticalSpace: \"2\",\n },\n};\n\n/** Build CSS variable map from a ThemeConfig. */\nexport function themeToCSS(theme: ThemeConfig): Record<string, string> {\n return {\n \"--chativa-primary-color\": theme.colors.primary,\n \"--chativa-secondary-color\": theme.colors.secondary,\n \"--chativa-background-color\": theme.colors.background,\n \"--chativa-text-color\": theme.colors.text,\n \"--chativa-border-color\": theme.colors.border,\n };\n}\n\n/** Deep merge a partial theme over a base theme. */\nexport function mergeTheme(\n base: ThemeConfig,\n overrides: DeepPartial<ThemeConfig>\n): ThemeConfig {\n return {\n ...base,\n ...overrides,\n colors: { ...base.colors, ...(overrides.colors ?? {}) },\n layout: { ...base.layout, ...(overrides.layout ?? {}) },\n } as ThemeConfig;\n}\n\nexport type DeepPartial<T> = {\n [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];\n};\n","import type { ISlashCommand, CommandContext } from \"../../domain/ports/ISlashCommand\";\n\nconst _commands = new Map<string, ISlashCommand>();\n\n/**\n * Registry for slash commands.\n * Commands are registered by extensions or app code and executed from ChatInput.\n * Built-in commands (e.g. /clear) are registered at the UI layer so they can\n * use the UI's i18n functions for translated descriptions.\n */\nexport const SlashCommandRegistry = {\n register(command: ISlashCommand): void {\n _commands.set(command.name, command);\n },\n\n unregister(name: string): void {\n _commands.delete(name);\n },\n\n get(name: string): ISlashCommand | undefined {\n return _commands.get(name);\n },\n\n list(): ISlashCommand[] {\n return Array.from(_commands.values());\n },\n\n execute(name: string, args: string): boolean {\n const cmd = _commands.get(name);\n if (!cmd) return false;\n const context: CommandContext = { args };\n cmd.execute(context);\n return true;\n },\n\n has(name: string): boolean {\n return _commands.has(name);\n },\n\n /** Reset all commands — for use in tests only. */\n clear(): void {\n _commands.clear();\n },\n};\n","import type { IExtension, ExtensionContext, MessageTransformer } from \"../../domain/ports/IExtension\";\nimport type { IncomingMessage, OutgoingMessage } from \"../../domain/entities/Message\";\nimport { SlashCommandRegistry } from \"./SlashCommandRegistry\";\n\n/** Internal pipeline storage. */\ninterface Pipeline {\n beforeSend: MessageTransformer<OutgoingMessage>[];\n afterReceive: MessageTransformer<IncomingMessage>[];\n onOpen: (() => void)[];\n onClose: (() => void)[];\n}\n\nconst extensions = new Map<string, IExtension>();\n\nconst pipeline: Pipeline = {\n beforeSend: [],\n afterReceive: [],\n onOpen: [],\n onClose: [],\n};\n\n/** Build an ExtensionContext scoped to a single extension. */\nfunction createContext(): ExtensionContext {\n return {\n onBeforeSend(handler) {\n pipeline.beforeSend.push(handler);\n },\n onAfterReceive(handler) {\n pipeline.afterReceive.push(handler);\n },\n onWidgetOpen(handler) {\n pipeline.onOpen.push(handler);\n },\n onWidgetClose(handler) {\n pipeline.onClose.push(handler);\n },\n registerCommand(command) {\n SlashCommandRegistry.register(command);\n },\n };\n}\n\nexport const ExtensionRegistry = {\n install(extension: IExtension): void {\n if (extensions.has(extension.name)) {\n throw new Error(\n `ExtensionRegistry: extension \"${extension.name}\" is already installed.`\n );\n }\n extensions.set(extension.name, extension);\n extension.install(createContext());\n },\n\n uninstall(name: string): void {\n const ext = extensions.get(name);\n if (!ext) return;\n ext.uninstall?.();\n extensions.delete(name);\n },\n\n /** Run the outgoing message through all beforeSend transformers. Returns null if blocked. */\n runBeforeSend(message: OutgoingMessage): OutgoingMessage | null {\n let msg: OutgoingMessage | null = message;\n for (const handler of pipeline.beforeSend) {\n if (msg === null) return null;\n msg = handler(msg);\n }\n return msg;\n },\n\n /** Run the incoming message through all afterReceive transformers. Returns null if blocked. */\n runAfterReceive(message: IncomingMessage): IncomingMessage | null {\n let msg: IncomingMessage | null = message;\n for (const handler of pipeline.afterReceive) {\n if (msg === null) return null;\n msg = handler(msg);\n }\n return msg;\n },\n\n notifyOpen(): void {\n pipeline.onOpen.forEach((h) => h());\n },\n\n notifyClose(): void {\n pipeline.onClose.forEach((h) => h());\n },\n\n has(name: string): boolean {\n return extensions.has(name);\n },\n\n list(): string[] {\n return [...extensions.keys()];\n },\n\n /** For testing only. */\n clear(): void {\n extensions.clear();\n pipeline.beforeSend = [];\n pipeline.afterReceive = [];\n pipeline.onOpen = [];\n pipeline.onClose = [];\n },\n};\n","/**\n * MessageTypeRegistry — maps message type strings to LitElement component constructors.\n *\n * Application layer: imports only from domain.\n */\n\ntype ComponentConstructor = typeof HTMLElement;\n\nlet fallbackComponent: ComponentConstructor | null = null;\n\nconst registry = new Map<string, ComponentConstructor>();\n\nexport const MessageTypeRegistry = {\n /**\n * Register a component constructor for a message type.\n * @param type The message type key (e.g. \"text\", \"card\", \"image\")\n * @param component The LitElement class to render for this type\n */\n register(type: string, component: ComponentConstructor): void {\n registry.set(type, component);\n },\n\n /**\n * Resolve a component for a given type.\n * Falls back to the registered fallback component (DefaultTextMessage).\n */\n resolve(type: string): ComponentConstructor {\n const component = registry.get(type);\n if (component) return component;\n if (fallbackComponent) return fallbackComponent;\n throw new Error(\n `MessageTypeRegistry: no component for type \"${type}\" and no fallback registered.`\n );\n },\n\n /**\n * Register the fallback component shown when no type match is found.\n */\n setFallback(component: ComponentConstructor): void {\n fallbackComponent = component;\n },\n\n has(type: string): boolean {\n return registry.has(type);\n },\n\n unregister(type: string): void {\n registry.delete(type);\n },\n\n /** For testing only. */\n clear(): void {\n registry.clear();\n fallbackComponent = null;\n },\n\n list(): string[] {\n return [...registry.keys()];\n },\n};\n","const createStoreImpl = (createState) => {\n let state;\n const listeners = /* @__PURE__ */ new Set();\n const setState = (partial, replace) => {\n const nextState = typeof partial === \"function\" ? partial(state) : partial;\n if (!Object.is(nextState, state)) {\n const previousState = state;\n state = (replace != null ? replace : typeof nextState !== \"object\" || nextState === null) ? nextState : Object.assign({}, state, nextState);\n listeners.forEach((listener) => listener(state, previousState));\n }\n };\n const getState = () => state;\n const getInitialState = () => initialState;\n const subscribe = (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n };\n const api = { setState, getState, getInitialState, subscribe };\n const initialState = state = createState(setState, getState, api);\n return api;\n};\nconst createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl);\n\nexport { createStore };\n","import { createStore } from \"zustand/vanilla\";\nimport type { IncomingMessage, MessageStatus } from \"../../domain/entities/Message\";\n\nexport interface StoredMessage extends IncomingMessage {\n component?: typeof HTMLElement;\n /** Delivery/read status (user messages only). */\n status?: MessageStatus;\n}\n\nexport interface MessageStoreState {\n messages: StoredMessage[];\n addMessage: (msg: StoredMessage) => void;\n prependMessages: (msgs: StoredMessage[]) => void;\n removeById: (id: string) => void;\n updateById: (id: string, patch: Partial<StoredMessage>) => void;\n clear: () => void;\n}\n\n/** Track rendered IDs to prevent duplicates. */\nconst renderedIds = new Set<string>();\n\nconst store = createStore<MessageStoreState>((setState) => ({\n messages: [],\n\n addMessage: (msg) =>\n setState((state) => {\n if (renderedIds.has(msg.id)) return state; // deduplicate\n renderedIds.add(msg.id);\n return { messages: [...state.messages, msg] };\n }),\n\n prependMessages: (msgs) =>\n setState((state) => {\n const fresh = msgs.filter((m) => !renderedIds.has(m.id));\n fresh.forEach((m) => renderedIds.add(m.id));\n return { messages: [...fresh, ...state.messages] };\n }),\n\n removeById: (id) =>\n setState((state) => ({\n messages: state.messages.filter((m) => m.id !== id),\n })),\n\n updateById: (id, patch) =>\n setState((state) => ({\n messages: state.messages.map((m) =>\n m.id === id ? { ...m, ...patch } : m\n ),\n })),\n\n clear: () => {\n renderedIds.clear();\n setState(() => ({ messages: [] }));\n },\n}));\n\nexport default store;\n","import { createStore } from \"zustand/vanilla\";\nimport { DEFAULT_THEME, mergeTheme, type ThemeConfig } from \"../../domain/value-objects/Theme\";\nimport type { DeepPartial } from \"../../domain/value-objects/Theme\";\n\n// Re-export for backwards compatibility\nexport type { ThemeConfig };\n\nexport type ConnectorStatus = \"idle\" | \"connecting\" | \"connected\" | \"error\" | \"disconnected\";\n\nexport interface ChatStoreState {\n isOpened: boolean;\n isRendered: boolean;\n isFullscreen: boolean;\n /** When false the fullscreen toggle button in the header is hidden. */\n allowFullscreen: boolean;\n activeConnector: string;\n connectorStatus: ConnectorStatus;\n /** True while the remote peer (bot) is composing a reply. */\n isTyping: boolean;\n /** Number of unread messages received while the chat was closed. */\n unreadCount: number;\n /** Current auto-reconnect attempt (0 = not reconnecting). */\n reconnectAttempt: number;\n theme: ThemeConfig;\n /** Whether the connector has older messages to load. */\n hasMoreHistory: boolean;\n /** True while a history page is being fetched. */\n isLoadingHistory: boolean;\n /** Opaque cursor for the next history page. */\n historyCursor?: string;\n\n toggle: () => void;\n open: () => void;\n close: () => void;\n toggleFullscreen: () => void;\n setFullscreen: (v: boolean) => void;\n setAllowFullscreen: (v: boolean) => void;\n\n setTheme: (theme: DeepPartial<ThemeConfig>) => void;\n getTheme: () => ThemeConfig;\n setConnector: (name: string) => void;\n setConnectorStatus: (status: ConnectorStatus) => void;\n setTyping: (v: boolean) => void;\n incrementUnread: () => void;\n resetUnread: () => void;\n setReconnectAttempt: (n: number) => void;\n setHasMoreHistory: (v: boolean) => void;\n setIsLoadingHistory: (v: boolean) => void;\n setHistoryCursor: (cursor: string | undefined) => void;\n subscribe: (cb: () => void) => () => void;\n}\n\n// Re-export for downstream consumers\nexport type { DeepPartial };\n\nconst store = createStore<ChatStoreState>((setState, getState) => ({\n isOpened: false,\n isRendered: false,\n isFullscreen: false,\n allowFullscreen: true,\n activeConnector: \"dummy\",\n connectorStatus: \"idle\",\n isTyping: false,\n unreadCount: 0,\n reconnectAttempt: 0,\n theme: DEFAULT_THEME,\n hasMoreHistory: false,\n isLoadingHistory: false,\n historyCursor: undefined,\n\n toggle: () =>\n setState((s) => ({\n isOpened: !s.isOpened,\n isRendered: true,\n unreadCount: 0,\n })),\n\n open: () => setState(() => ({ isOpened: true, isRendered: true, unreadCount: 0 })),\n close: () => setState(() => ({ isOpened: false })),\n\n toggleFullscreen: () =>\n setState((s) => ({ isFullscreen: !s.isFullscreen })),\n\n setFullscreen: (v: boolean) => setState(() => ({ isFullscreen: v })),\n\n setAllowFullscreen: (v: boolean) => setState(() => ({ allowFullscreen: v })),\n\n setTheme: (overrides) =>\n setState((s) => ({ theme: mergeTheme(s.theme, overrides) })),\n\n getTheme: () => getState().theme,\n\n setConnector: (name: string) => setState(() => ({ activeConnector: name })),\n\n setConnectorStatus: (status: ConnectorStatus) =>\n setState(() => ({ connectorStatus: status })),\n\n setTyping: (v: boolean) => setState(() => ({ isTyping: v })),\n\n incrementUnread: () => setState((s) => ({ unreadCount: s.unreadCount + 1 })),\n resetUnread: () => setState(() => ({ unreadCount: 0 })),\n setReconnectAttempt: (n: number) => setState(() => ({ reconnectAttempt: n })),\n setHasMoreHistory: (v: boolean) => setState(() => ({ hasMoreHistory: v })),\n setIsLoadingHistory: (v: boolean) => setState(() => ({ isLoadingHistory: v })),\n setHistoryCursor: (cursor: string | undefined) => setState(() => ({ historyCursor: cursor })),\n\n subscribe: (cb: () => void): (() => void) =>\n store.subscribe(() => cb()),\n}));\n\nexport default store;\n","import type { IConnector, FeedbackType } from \"../domain/ports/IConnector\";\nimport type { OutgoingMessage } from \"../domain/entities/Message\";\nimport { ExtensionRegistry } from \"./registries/ExtensionRegistry\";\nimport { MessageTypeRegistry } from \"./registries/MessageTypeRegistry\";\nimport messageStore from \"./stores/MessageStore\";\nimport chatStore from \"./stores/ChatStore\";\n\nexport class ChatEngine {\n private connector: IConnector;\n private _reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private _destroyed = false;\n\n constructor(connector: IConnector) {\n this.connector = connector;\n }\n\n async init(): Promise<void> {\n this._destroyed = false;\n\n this.connector.onMessage((msg) => {\n chatStore.getState().setTyping(false);\n\n const transformed = ExtensionRegistry.runAfterReceive(msg);\n if (transformed === null) return; // extension blocked it\n\n // Increment unread count when chat is closed\n if (!chatStore.getState().isOpened) {\n chatStore.getState().incrementUnread();\n }\n\n const Component = MessageTypeRegistry.resolve(transformed.type);\n messageStore.getState().addMessage({ ...transformed, from: \"bot\", component: Component });\n });\n\n this.connector.onDisconnect?.((reason) => {\n chatStore.getState().setConnectorStatus(\"disconnected\");\n // Auto-reconnect unless destroyed or user-initiated disconnect\n if (!this._destroyed && reason !== \"user\") {\n this._scheduleReconnect(1);\n }\n });\n\n this.connector.onTyping?.((isTyping) => {\n chatStore.getState().setTyping(isTyping);\n });\n\n this.connector.onMessageStatus?.((messageId, status) => {\n messageStore.getState().updateById(messageId, { status });\n });\n\n chatStore.getState().setConnectorStatus(\"connecting\");\n try {\n await this.connector.connect();\n chatStore.getState().setConnectorStatus(\"connected\");\n // Load initial history if supported\n if (this.connector.loadHistory) {\n await this.loadHistory();\n }\n } catch (err) {\n chatStore.getState().setConnectorStatus(\"error\");\n throw err;\n }\n }\n\n private _scheduleReconnect(attempt: number): void {\n if (this._destroyed || attempt > 3) {\n chatStore.getState().setConnectorStatus(\"error\");\n chatStore.getState().setReconnectAttempt(0);\n return;\n }\n\n chatStore.getState().setReconnectAttempt(attempt);\n chatStore.getState().setConnectorStatus(\"connecting\");\n\n const delay = 2000 * attempt; // 2s, 4s, 6s\n this._reconnectTimer = setTimeout(async () => {\n if (this._destroyed) return;\n try {\n await this.connector.connect();\n chatStore.getState().setConnectorStatus(\"connected\");\n chatStore.getState().setReconnectAttempt(0);\n } catch {\n this._scheduleReconnect(attempt + 1);\n }\n }, delay);\n }\n\n async sendFeedback(messageId: string, feedback: FeedbackType): Promise<void> {\n await this.connector.sendFeedback?.(messageId, feedback);\n }\n\n async send(message: OutgoingMessage): Promise<void> {\n const transformed = ExtensionRegistry.runBeforeSend(message);\n if (transformed === null) return; // extension blocked it\n\n if (this.connector.addSentToHistory !== false) {\n const Component = MessageTypeRegistry.resolve(transformed.type);\n messageStore.getState().addMessage({\n ...transformed,\n from: \"user\",\n component: Component,\n status: \"sending\",\n });\n }\n\n await this.connector.sendMessage(transformed);\n\n if (this.connector.addSentToHistory !== false) {\n messageStore.getState().updateById(transformed.id, { status: \"sent\" });\n }\n }\n\n async sendFile(file: File, metadata?: Record<string, unknown>): Promise<void> {\n await this.connector.sendFile?.(file, metadata);\n }\n\n async loadHistory(): Promise<void> {\n if (!this.connector.loadHistory) return;\n const { isLoadingHistory, historyCursor } = chatStore.getState();\n if (isLoadingHistory) return;\n\n chatStore.getState().setIsLoadingHistory(true);\n try {\n const result = await this.connector.loadHistory(historyCursor);\n const msgs = result.messages.map((m) => ({\n ...m,\n from: (m.from ?? \"bot\") as \"bot\" | \"user\",\n component: MessageTypeRegistry.resolve(m.type),\n }));\n messageStore.getState().prependMessages(msgs);\n chatStore.getState().setHasMoreHistory(result.hasMore);\n chatStore.getState().setHistoryCursor(result.cursor);\n } finally {\n chatStore.getState().setIsLoadingHistory(false);\n }\n }\n\n async destroy(): Promise<void> {\n this._destroyed = true;\n if (this._reconnectTimer !== null) {\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = null;\n }\n chatStore.getState().setReconnectAttempt(0);\n await this.connector.disconnect();\n chatStore.getState().setConnectorStatus(\"disconnected\");\n }\n}\n","import type { IConnector } from \"../../domain/ports/IConnector\";\n\nconst registry = new Map<string, IConnector>();\n\nexport const ConnectorRegistry = {\n register(connector: IConnector): void {\n if (registry.has(connector.name)) {\n throw new Error(\n `ConnectorRegistry: connector \"${connector.name}\" is already registered. ` +\n `Call ConnectorRegistry.unregister(\"${connector.name}\") first.`\n );\n }\n registry.set(connector.name, connector);\n },\n\n get(name: string): IConnector {\n const connector = registry.get(name);\n if (!connector) {\n throw new Error(\n `ConnectorRegistry: connector \"${name}\" not found. ` +\n `Available: [${[...registry.keys()].join(\", \")}]`\n );\n }\n return connector;\n },\n\n has(name: string): boolean {\n return registry.has(name);\n },\n\n unregister(name: string): void {\n registry.delete(name);\n },\n\n /** For testing only — clears all registered connectors. */\n clear(): void {\n registry.clear();\n },\n\n list(): string[] {\n return [...registry.keys()];\n },\n};\n"],"names":["createOutgoingMessage","text","type","resolveText","value","DEFAULT_THEME","themeToCSS","theme","mergeTheme","base","overrides","_commands","SlashCommandRegistry","command","name","args","cmd","context","extensions","pipeline","createContext","handler","ExtensionRegistry","extension","ext","message","msg","h","fallbackComponent","registry","MessageTypeRegistry","component","createStoreImpl","createState","state","listeners","setState","partial","replace","nextState","previousState","listener","getState","api","initialState","createStore","renderedIds","store","msgs","fresh","m","id","patch","s","v","status","cursor","cb","ChatEngine","connector","chatStore","transformed","Component","messageStore","reason","isTyping","messageId","err","attempt","delay","feedback","file","metadata","isLoadingHistory","historyCursor","result","ConnectorRegistry"],"mappings":"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","x_google_ignoreList":[6]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { StoreApi } from 'zustand/vanilla';
|
|
2
|
+
|
|
3
|
+
/** Avatar configuration for bot and user sides. */
|
|
4
|
+
export declare interface AvatarConfig {
|
|
5
|
+
/** URL for the bot avatar image. Omit to use the default robot SVG. */
|
|
6
|
+
bot?: string;
|
|
7
|
+
/** URL for the user avatar image. Omit to use the default person SVG. */
|
|
8
|
+
user?: string;
|
|
9
|
+
/** Show bot avatar. Default: true. */
|
|
10
|
+
showBot?: boolean;
|
|
11
|
+
/** Show user avatar. Default: true. */
|
|
12
|
+
showUser?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Theme value object — pure types, no logic.
|
|
17
|
+
* No external dependencies allowed.
|
|
18
|
+
*/
|
|
19
|
+
export declare type ButtonPosition = "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
20
|
+
|
|
21
|
+
export declare type ButtonSize = "small" | "medium" | "large";
|
|
22
|
+
|
|
23
|
+
export declare class ChatEngine {
|
|
24
|
+
private connector;
|
|
25
|
+
private _reconnectTimer;
|
|
26
|
+
private _destroyed;
|
|
27
|
+
constructor(connector: IConnector);
|
|
28
|
+
init(): Promise<void>;
|
|
29
|
+
private _scheduleReconnect;
|
|
30
|
+
sendFeedback(messageId: string, feedback: FeedbackType): Promise<void>;
|
|
31
|
+
send(message: OutgoingMessage): Promise<void>;
|
|
32
|
+
sendFile(file: File, metadata?: Record<string, unknown>): Promise<void>;
|
|
33
|
+
loadHistory(): Promise<void>;
|
|
34
|
+
destroy(): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export declare const chatStore: StoreApi<ChatStoreState>;
|
|
38
|
+
|
|
39
|
+
export declare interface ChatStoreState {
|
|
40
|
+
isOpened: boolean;
|
|
41
|
+
isRendered: boolean;
|
|
42
|
+
isFullscreen: boolean;
|
|
43
|
+
/** When false the fullscreen toggle button in the header is hidden. */
|
|
44
|
+
allowFullscreen: boolean;
|
|
45
|
+
activeConnector: string;
|
|
46
|
+
connectorStatus: ConnectorStatus;
|
|
47
|
+
/** True while the remote peer (bot) is composing a reply. */
|
|
48
|
+
isTyping: boolean;
|
|
49
|
+
/** Number of unread messages received while the chat was closed. */
|
|
50
|
+
unreadCount: number;
|
|
51
|
+
/** Current auto-reconnect attempt (0 = not reconnecting). */
|
|
52
|
+
reconnectAttempt: number;
|
|
53
|
+
theme: ThemeConfig;
|
|
54
|
+
/** Whether the connector has older messages to load. */
|
|
55
|
+
hasMoreHistory: boolean;
|
|
56
|
+
/** True while a history page is being fetched. */
|
|
57
|
+
isLoadingHistory: boolean;
|
|
58
|
+
/** Opaque cursor for the next history page. */
|
|
59
|
+
historyCursor?: string;
|
|
60
|
+
toggle: () => void;
|
|
61
|
+
open: () => void;
|
|
62
|
+
close: () => void;
|
|
63
|
+
toggleFullscreen: () => void;
|
|
64
|
+
setFullscreen: (v: boolean) => void;
|
|
65
|
+
setAllowFullscreen: (v: boolean) => void;
|
|
66
|
+
setTheme: (theme: DeepPartial<ThemeConfig>) => void;
|
|
67
|
+
getTheme: () => ThemeConfig;
|
|
68
|
+
setConnector: (name: string) => void;
|
|
69
|
+
setConnectorStatus: (status: ConnectorStatus) => void;
|
|
70
|
+
setTyping: (v: boolean) => void;
|
|
71
|
+
incrementUnread: () => void;
|
|
72
|
+
resetUnread: () => void;
|
|
73
|
+
setReconnectAttempt: (n: number) => void;
|
|
74
|
+
setHasMoreHistory: (v: boolean) => void;
|
|
75
|
+
setIsLoadingHistory: (v: boolean) => void;
|
|
76
|
+
setHistoryCursor: (cursor: string | undefined) => void;
|
|
77
|
+
subscribe: (cb: () => void) => () => void;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* ISlashCommand — Port definition for slash commands.
|
|
82
|
+
* No external dependencies allowed in this file.
|
|
83
|
+
*/
|
|
84
|
+
export declare interface CommandContext {
|
|
85
|
+
/** Everything typed after the command name (trimmed). */
|
|
86
|
+
args: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* MessageTypeRegistry — maps message type strings to LitElement component constructors.
|
|
91
|
+
*
|
|
92
|
+
* Application layer: imports only from domain.
|
|
93
|
+
*/
|
|
94
|
+
declare type ComponentConstructor = typeof HTMLElement;
|
|
95
|
+
|
|
96
|
+
export declare type ConnectHandler = () => void;
|
|
97
|
+
|
|
98
|
+
export declare const ConnectorRegistry: {
|
|
99
|
+
register(connector: IConnector): void;
|
|
100
|
+
get(name: string): IConnector;
|
|
101
|
+
has(name: string): boolean;
|
|
102
|
+
unregister(name: string): void;
|
|
103
|
+
/** For testing only — clears all registered connectors. */
|
|
104
|
+
clear(): void;
|
|
105
|
+
list(): string[];
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export declare type ConnectorStatus = "idle" | "connecting" | "connected" | "error" | "disconnected";
|
|
109
|
+
|
|
110
|
+
export declare function createOutgoingMessage(text: string, type?: string): OutgoingMessage;
|
|
111
|
+
|
|
112
|
+
export declare type DeepPartial<T> = {
|
|
113
|
+
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export declare const DEFAULT_THEME: ThemeConfig;
|
|
117
|
+
|
|
118
|
+
export declare type DisconnectHandler = (reason?: string) => void;
|
|
119
|
+
|
|
120
|
+
export declare interface ExtensionContext {
|
|
121
|
+
/** Hook called before a message is sent. Return null to cancel. */
|
|
122
|
+
onBeforeSend(handler: MessageTransformer<OutgoingMessage>): void;
|
|
123
|
+
/** Hook called after a message is received. Return null to drop. */
|
|
124
|
+
onAfterReceive(handler: MessageTransformer<IncomingMessage>): void;
|
|
125
|
+
/** Hook called when the chat widget opens. */
|
|
126
|
+
onWidgetOpen(handler: () => void): void;
|
|
127
|
+
/** Hook called when the chat widget closes. */
|
|
128
|
+
onWidgetClose(handler: () => void): void;
|
|
129
|
+
/** Register a slash command that users can invoke from the chat input. */
|
|
130
|
+
registerCommand(command: ISlashCommand): void;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export declare const ExtensionRegistry: {
|
|
134
|
+
install(extension: IExtension): void;
|
|
135
|
+
uninstall(name: string): void;
|
|
136
|
+
/** Run the outgoing message through all beforeSend transformers. Returns null if blocked. */
|
|
137
|
+
runBeforeSend(message: OutgoingMessage): OutgoingMessage | null;
|
|
138
|
+
/** Run the incoming message through all afterReceive transformers. Returns null if blocked. */
|
|
139
|
+
runAfterReceive(message: IncomingMessage): IncomingMessage | null;
|
|
140
|
+
notifyOpen(): void;
|
|
141
|
+
notifyClose(): void;
|
|
142
|
+
has(name: string): boolean;
|
|
143
|
+
list(): string[];
|
|
144
|
+
/** For testing only. */
|
|
145
|
+
clear(): void;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export declare type FeedbackType = "like" | "dislike";
|
|
149
|
+
|
|
150
|
+
/** Result returned by IConnector.loadHistory(). */
|
|
151
|
+
export declare interface HistoryResult {
|
|
152
|
+
messages: IncomingMessage[];
|
|
153
|
+
hasMore: boolean;
|
|
154
|
+
/** Opaque cursor passed back to load the next (older) page. */
|
|
155
|
+
cursor?: string;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export declare interface IConnector {
|
|
159
|
+
/** Unique identifier used to select this connector at runtime. */
|
|
160
|
+
readonly name: string;
|
|
161
|
+
/**
|
|
162
|
+
* Whether sent messages should be added to chat history immediately.
|
|
163
|
+
* Set to false for connectors like DirectLine where the bot echoes back.
|
|
164
|
+
* Default: true
|
|
165
|
+
*/
|
|
166
|
+
readonly addSentToHistory?: boolean;
|
|
167
|
+
/** Establish connection to the backend. */
|
|
168
|
+
connect(): Promise<void>;
|
|
169
|
+
/** Gracefully close the connection. */
|
|
170
|
+
disconnect(): Promise<void>;
|
|
171
|
+
/** Send a message to the backend. */
|
|
172
|
+
sendMessage(message: OutgoingMessage): Promise<void>;
|
|
173
|
+
/** Register a callback for incoming messages. */
|
|
174
|
+
onMessage(callback: MessageHandler): void;
|
|
175
|
+
/** Optional: called when connection is established. */
|
|
176
|
+
onConnect?(callback: ConnectHandler): void;
|
|
177
|
+
/** Optional: called when connection is lost. */
|
|
178
|
+
onDisconnect?(callback: DisconnectHandler): void;
|
|
179
|
+
/** Optional: called when the remote peer starts or stops typing. */
|
|
180
|
+
onTyping?(callback: TypingHandler): void;
|
|
181
|
+
/** Optional: send a like/dislike reaction on a bot message to the backend. */
|
|
182
|
+
sendFeedback?(messageId: string, feedback: FeedbackType): Promise<void>;
|
|
183
|
+
/** Optional: upload a file to the backend. */
|
|
184
|
+
sendFile?(file: File, metadata?: Record<string, unknown>): Promise<void>;
|
|
185
|
+
/** Optional: load older messages. Returns a page of history and whether more exist. */
|
|
186
|
+
loadHistory?(cursor?: string): Promise<HistoryResult>;
|
|
187
|
+
/** Optional: register a callback for message delivery/read status updates. */
|
|
188
|
+
onMessageStatus?(callback: MessageStatusHandler): void;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export declare interface IExtension {
|
|
192
|
+
/** Unique extension name. */
|
|
193
|
+
readonly name: string;
|
|
194
|
+
/** Semantic version string. */
|
|
195
|
+
readonly version: string;
|
|
196
|
+
/** Called once when the extension is installed. */
|
|
197
|
+
install(context: ExtensionContext): void;
|
|
198
|
+
/** Called when the extension is uninstalled. */
|
|
199
|
+
uninstall?(): void;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* IMessageRenderer — Contract for message display components.
|
|
204
|
+
*
|
|
205
|
+
* Every custom message type component must satisfy this interface.
|
|
206
|
+
* No external dependencies allowed in this file.
|
|
207
|
+
*/
|
|
208
|
+
export declare interface IMessageRenderer {
|
|
209
|
+
/** The message type key this renderer handles (e.g. "text", "card"). */
|
|
210
|
+
readonly messageType?: string;
|
|
211
|
+
/** The message data payload passed from the incoming message. */
|
|
212
|
+
messageData: Record<string, unknown>;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export declare interface IncomingMessage {
|
|
216
|
+
id: string;
|
|
217
|
+
type: string;
|
|
218
|
+
from?: MessageSender;
|
|
219
|
+
data: Record<string, unknown>;
|
|
220
|
+
timestamp?: number;
|
|
221
|
+
/** Optional quick-reply chips rendered below the message bubble. */
|
|
222
|
+
actions?: MessageAction[];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export declare interface ISlashCommand {
|
|
226
|
+
/** Command name without the leading slash, e.g. "clear". */
|
|
227
|
+
readonly name: string;
|
|
228
|
+
/**
|
|
229
|
+
* Short description shown in the autocomplete popup.
|
|
230
|
+
* Pass a function for lazy i18n evaluation at render time:
|
|
231
|
+
* `description: () => t("commands.clear.description")`
|
|
232
|
+
*/
|
|
233
|
+
readonly description: string | (() => string);
|
|
234
|
+
/**
|
|
235
|
+
* Optional usage hint displayed next to the command name.
|
|
236
|
+
* Shows accepted parameters, e.g. `"[count]"` or `"<text>"`.
|
|
237
|
+
* Also supports lazy i18n: `() => t("commands.clear.usage")`
|
|
238
|
+
*/
|
|
239
|
+
readonly usage?: string | (() => string);
|
|
240
|
+
execute(context: CommandContext): void;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export declare interface LayoutConfig {
|
|
244
|
+
width?: string;
|
|
245
|
+
height?: string;
|
|
246
|
+
maxWidth?: string;
|
|
247
|
+
maxHeight?: string;
|
|
248
|
+
horizontalSpace?: SpaceLevel;
|
|
249
|
+
verticalSpace?: SpaceLevel;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** Deep merge a partial theme over a base theme. */
|
|
253
|
+
export declare function mergeTheme(base: ThemeConfig, overrides: DeepPartial<ThemeConfig>): ThemeConfig;
|
|
254
|
+
|
|
255
|
+
/** A tappable chip/button sent alongside a bot message. */
|
|
256
|
+
export declare interface MessageAction {
|
|
257
|
+
label: string;
|
|
258
|
+
/** Text to send when tapped; falls back to `label` if omitted. */
|
|
259
|
+
value?: string;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export declare type MessageHandler = (message: IncomingMessage) => void;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Domain entities for messages.
|
|
266
|
+
* No external dependencies allowed in this file.
|
|
267
|
+
*/
|
|
268
|
+
export declare type MessageSender = "user" | "bot";
|
|
269
|
+
|
|
270
|
+
/** Delivery/read status for a message. */
|
|
271
|
+
export declare type MessageStatus = "sending" | "sent" | "read";
|
|
272
|
+
|
|
273
|
+
export declare type MessageStatusHandler = (messageId: string, status: MessageStatus) => void;
|
|
274
|
+
|
|
275
|
+
export declare const messageStore: StoreApi<MessageStoreState>;
|
|
276
|
+
|
|
277
|
+
export declare interface MessageStoreState {
|
|
278
|
+
messages: StoredMessage[];
|
|
279
|
+
addMessage: (msg: StoredMessage) => void;
|
|
280
|
+
prependMessages: (msgs: StoredMessage[]) => void;
|
|
281
|
+
removeById: (id: string) => void;
|
|
282
|
+
updateById: (id: string, patch: Partial<StoredMessage>) => void;
|
|
283
|
+
clear: () => void;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export declare type MessageTransformer<T extends IncomingMessage | OutgoingMessage> = (message: T) => T | null;
|
|
287
|
+
|
|
288
|
+
export declare const MessageTypeRegistry: {
|
|
289
|
+
/**
|
|
290
|
+
* Register a component constructor for a message type.
|
|
291
|
+
* @param type The message type key (e.g. "text", "card", "image")
|
|
292
|
+
* @param component The LitElement class to render for this type
|
|
293
|
+
*/
|
|
294
|
+
register(type: string, component: ComponentConstructor): void;
|
|
295
|
+
/**
|
|
296
|
+
* Resolve a component for a given type.
|
|
297
|
+
* Falls back to the registered fallback component (DefaultTextMessage).
|
|
298
|
+
*/
|
|
299
|
+
resolve(type: string): ComponentConstructor;
|
|
300
|
+
/**
|
|
301
|
+
* Register the fallback component shown when no type match is found.
|
|
302
|
+
*/
|
|
303
|
+
setFallback(component: ComponentConstructor): void;
|
|
304
|
+
has(type: string): boolean;
|
|
305
|
+
unregister(type: string): void;
|
|
306
|
+
/** For testing only. */
|
|
307
|
+
clear(): void;
|
|
308
|
+
list(): string[];
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
export declare interface OutgoingMessage {
|
|
312
|
+
id: string;
|
|
313
|
+
type: string;
|
|
314
|
+
data: Record<string, unknown>;
|
|
315
|
+
timestamp?: number;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/** Resolve a description or usage value, calling it if it is a function. */
|
|
319
|
+
export declare function resolveText(value: string | (() => string) | undefined): string;
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Registry for slash commands.
|
|
323
|
+
* Commands are registered by extensions or app code and executed from ChatInput.
|
|
324
|
+
* Built-in commands (e.g. /clear) are registered at the UI layer so they can
|
|
325
|
+
* use the UI's i18n functions for translated descriptions.
|
|
326
|
+
*/
|
|
327
|
+
export declare const SlashCommandRegistry: {
|
|
328
|
+
register(command: ISlashCommand): void;
|
|
329
|
+
unregister(name: string): void;
|
|
330
|
+
get(name: string): ISlashCommand | undefined;
|
|
331
|
+
list(): ISlashCommand[];
|
|
332
|
+
execute(name: string, args: string): boolean;
|
|
333
|
+
has(name: string): boolean;
|
|
334
|
+
/** Reset all commands — for use in tests only. */
|
|
335
|
+
clear(): void;
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
export declare type SpaceLevel = "1" | "2" | "3" | "4" | "5";
|
|
339
|
+
|
|
340
|
+
export declare interface StoredMessage extends IncomingMessage {
|
|
341
|
+
component?: typeof HTMLElement;
|
|
342
|
+
/** Delivery/read status (user messages only). */
|
|
343
|
+
status?: MessageStatus;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export declare interface ThemeColors {
|
|
347
|
+
primary: string;
|
|
348
|
+
secondary: string;
|
|
349
|
+
background: string;
|
|
350
|
+
text: string;
|
|
351
|
+
border: string;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export declare interface ThemeConfig {
|
|
355
|
+
colors: ThemeColors;
|
|
356
|
+
position: ButtonPosition;
|
|
357
|
+
positionMargin?: SpaceLevel;
|
|
358
|
+
size: ButtonSize;
|
|
359
|
+
layout: LayoutConfig;
|
|
360
|
+
/** Avatar configuration for bot and user sides. */
|
|
361
|
+
avatar?: AvatarConfig;
|
|
362
|
+
/** Show delivery/read status ticks on user messages. Default: false. */
|
|
363
|
+
showMessageStatus?: boolean;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** Build CSS variable map from a ThemeConfig. */
|
|
367
|
+
export declare function themeToCSS(theme: ThemeConfig): Record<string, string>;
|
|
368
|
+
|
|
369
|
+
export declare type TypingHandler = (isTyping: boolean) => void;
|
|
370
|
+
|
|
371
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
function A(e, t = "text") {
|
|
2
|
+
return {
|
|
3
|
+
id: `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
4
|
+
type: t,
|
|
5
|
+
data: { text: e },
|
|
6
|
+
timestamp: Date.now()
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
function E(e) {
|
|
10
|
+
return e === void 0 ? "" : typeof e == "function" ? e() : e;
|
|
11
|
+
}
|
|
12
|
+
const v = {
|
|
13
|
+
colors: {
|
|
14
|
+
primary: "#4f46e5",
|
|
15
|
+
secondary: "#6c757d",
|
|
16
|
+
background: "#ffffff",
|
|
17
|
+
text: "#212529",
|
|
18
|
+
border: "#dee2e6"
|
|
19
|
+
},
|
|
20
|
+
position: "bottom-right",
|
|
21
|
+
positionMargin: "2",
|
|
22
|
+
size: "medium",
|
|
23
|
+
layout: {
|
|
24
|
+
width: "360px",
|
|
25
|
+
height: "520px",
|
|
26
|
+
maxWidth: "100%",
|
|
27
|
+
maxHeight: "100%",
|
|
28
|
+
horizontalSpace: "2",
|
|
29
|
+
verticalSpace: "2"
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
function k(e) {
|
|
33
|
+
return {
|
|
34
|
+
"--chativa-primary-color": e.colors.primary,
|
|
35
|
+
"--chativa-secondary-color": e.colors.secondary,
|
|
36
|
+
"--chativa-background-color": e.colors.background,
|
|
37
|
+
"--chativa-text-color": e.colors.text,
|
|
38
|
+
"--chativa-border-color": e.colors.border
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function x(e, t) {
|
|
42
|
+
return {
|
|
43
|
+
...e,
|
|
44
|
+
...t,
|
|
45
|
+
colors: { ...e.colors, ...t.colors ?? {} },
|
|
46
|
+
layout: { ...e.layout, ...t.layout ?? {} }
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const a = /* @__PURE__ */ new Map(), _ = {
|
|
50
|
+
register(e) {
|
|
51
|
+
a.set(e.name, e);
|
|
52
|
+
},
|
|
53
|
+
unregister(e) {
|
|
54
|
+
a.delete(e);
|
|
55
|
+
},
|
|
56
|
+
get(e) {
|
|
57
|
+
return a.get(e);
|
|
58
|
+
},
|
|
59
|
+
list() {
|
|
60
|
+
return Array.from(a.values());
|
|
61
|
+
},
|
|
62
|
+
execute(e, t) {
|
|
63
|
+
const n = a.get(e);
|
|
64
|
+
if (!n) return !1;
|
|
65
|
+
const r = { args: t };
|
|
66
|
+
return n.execute(r), !0;
|
|
67
|
+
},
|
|
68
|
+
has(e) {
|
|
69
|
+
return a.has(e);
|
|
70
|
+
},
|
|
71
|
+
/** Reset all commands — for use in tests only. */
|
|
72
|
+
clear() {
|
|
73
|
+
a.clear();
|
|
74
|
+
}
|
|
75
|
+
}, l = /* @__PURE__ */ new Map(), c = {
|
|
76
|
+
beforeSend: [],
|
|
77
|
+
afterReceive: [],
|
|
78
|
+
onOpen: [],
|
|
79
|
+
onClose: []
|
|
80
|
+
};
|
|
81
|
+
function F() {
|
|
82
|
+
return {
|
|
83
|
+
onBeforeSend(e) {
|
|
84
|
+
c.beforeSend.push(e);
|
|
85
|
+
},
|
|
86
|
+
onAfterReceive(e) {
|
|
87
|
+
c.afterReceive.push(e);
|
|
88
|
+
},
|
|
89
|
+
onWidgetOpen(e) {
|
|
90
|
+
c.onOpen.push(e);
|
|
91
|
+
},
|
|
92
|
+
onWidgetClose(e) {
|
|
93
|
+
c.onClose.push(e);
|
|
94
|
+
},
|
|
95
|
+
registerCommand(e) {
|
|
96
|
+
_.register(e);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const w = {
|
|
101
|
+
install(e) {
|
|
102
|
+
if (l.has(e.name))
|
|
103
|
+
throw new Error(
|
|
104
|
+
`ExtensionRegistry: extension "${e.name}" is already installed.`
|
|
105
|
+
);
|
|
106
|
+
l.set(e.name, e), e.install(F());
|
|
107
|
+
},
|
|
108
|
+
uninstall(e) {
|
|
109
|
+
const t = l.get(e);
|
|
110
|
+
t && (t.uninstall?.(), l.delete(e));
|
|
111
|
+
},
|
|
112
|
+
/** Run the outgoing message through all beforeSend transformers. Returns null if blocked. */
|
|
113
|
+
runBeforeSend(e) {
|
|
114
|
+
let t = e;
|
|
115
|
+
for (const n of c.beforeSend) {
|
|
116
|
+
if (t === null) return null;
|
|
117
|
+
t = n(t);
|
|
118
|
+
}
|
|
119
|
+
return t;
|
|
120
|
+
},
|
|
121
|
+
/** Run the incoming message through all afterReceive transformers. Returns null if blocked. */
|
|
122
|
+
runAfterReceive(e) {
|
|
123
|
+
let t = e;
|
|
124
|
+
for (const n of c.afterReceive) {
|
|
125
|
+
if (t === null) return null;
|
|
126
|
+
t = n(t);
|
|
127
|
+
}
|
|
128
|
+
return t;
|
|
129
|
+
},
|
|
130
|
+
notifyOpen() {
|
|
131
|
+
c.onOpen.forEach((e) => e());
|
|
132
|
+
},
|
|
133
|
+
notifyClose() {
|
|
134
|
+
c.onClose.forEach((e) => e());
|
|
135
|
+
},
|
|
136
|
+
has(e) {
|
|
137
|
+
return l.has(e);
|
|
138
|
+
},
|
|
139
|
+
list() {
|
|
140
|
+
return [...l.keys()];
|
|
141
|
+
},
|
|
142
|
+
/** For testing only. */
|
|
143
|
+
clear() {
|
|
144
|
+
l.clear(), c.beforeSend = [], c.afterReceive = [], c.onOpen = [], c.onClose = [];
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
let m = null;
|
|
148
|
+
const u = /* @__PURE__ */ new Map(), p = {
|
|
149
|
+
/**
|
|
150
|
+
* Register a component constructor for a message type.
|
|
151
|
+
* @param type The message type key (e.g. "text", "card", "image")
|
|
152
|
+
* @param component The LitElement class to render for this type
|
|
153
|
+
*/
|
|
154
|
+
register(e, t) {
|
|
155
|
+
u.set(e, t);
|
|
156
|
+
},
|
|
157
|
+
/**
|
|
158
|
+
* Resolve a component for a given type.
|
|
159
|
+
* Falls back to the registered fallback component (DefaultTextMessage).
|
|
160
|
+
*/
|
|
161
|
+
resolve(e) {
|
|
162
|
+
const t = u.get(e);
|
|
163
|
+
if (t) return t;
|
|
164
|
+
if (m) return m;
|
|
165
|
+
throw new Error(
|
|
166
|
+
`MessageTypeRegistry: no component for type "${e}" and no fallback registered.`
|
|
167
|
+
);
|
|
168
|
+
},
|
|
169
|
+
/**
|
|
170
|
+
* Register the fallback component shown when no type match is found.
|
|
171
|
+
*/
|
|
172
|
+
setFallback(e) {
|
|
173
|
+
m = e;
|
|
174
|
+
},
|
|
175
|
+
has(e) {
|
|
176
|
+
return u.has(e);
|
|
177
|
+
},
|
|
178
|
+
unregister(e) {
|
|
179
|
+
u.delete(e);
|
|
180
|
+
},
|
|
181
|
+
/** For testing only. */
|
|
182
|
+
clear() {
|
|
183
|
+
u.clear(), m = null;
|
|
184
|
+
},
|
|
185
|
+
list() {
|
|
186
|
+
return [...u.keys()];
|
|
187
|
+
}
|
|
188
|
+
}, b = (e) => {
|
|
189
|
+
let t;
|
|
190
|
+
const n = /* @__PURE__ */ new Set(), r = (d, C) => {
|
|
191
|
+
const g = typeof d == "function" ? d(t) : d;
|
|
192
|
+
if (!Object.is(g, t)) {
|
|
193
|
+
const H = t;
|
|
194
|
+
t = C ?? (typeof g != "object" || g === null) ? g : Object.assign({}, t, g), n.forEach((M) => M(t, H));
|
|
195
|
+
}
|
|
196
|
+
}, o = () => t, S = { setState: r, getState: o, getInitialState: () => T, subscribe: (d) => (n.add(d), () => n.delete(d)) }, T = t = e(r, o, S);
|
|
197
|
+
return S;
|
|
198
|
+
}, R = ((e) => e ? b(e) : b), f = /* @__PURE__ */ new Set(), y = R((e) => ({
|
|
199
|
+
messages: [],
|
|
200
|
+
addMessage: (t) => e((n) => f.has(t.id) ? n : (f.add(t.id), { messages: [...n.messages, t] })),
|
|
201
|
+
prependMessages: (t) => e((n) => {
|
|
202
|
+
const r = t.filter((o) => !f.has(o.id));
|
|
203
|
+
return r.forEach((o) => f.add(o.id)), { messages: [...r, ...n.messages] };
|
|
204
|
+
}),
|
|
205
|
+
removeById: (t) => e((n) => ({
|
|
206
|
+
messages: n.messages.filter((r) => r.id !== t)
|
|
207
|
+
})),
|
|
208
|
+
updateById: (t, n) => e((r) => ({
|
|
209
|
+
messages: r.messages.map(
|
|
210
|
+
(o) => o.id === t ? { ...o, ...n } : o
|
|
211
|
+
)
|
|
212
|
+
})),
|
|
213
|
+
clear: () => {
|
|
214
|
+
f.clear(), e(() => ({ messages: [] }));
|
|
215
|
+
}
|
|
216
|
+
})), s = R((e, t) => ({
|
|
217
|
+
isOpened: !1,
|
|
218
|
+
isRendered: !1,
|
|
219
|
+
isFullscreen: !1,
|
|
220
|
+
allowFullscreen: !0,
|
|
221
|
+
activeConnector: "dummy",
|
|
222
|
+
connectorStatus: "idle",
|
|
223
|
+
isTyping: !1,
|
|
224
|
+
unreadCount: 0,
|
|
225
|
+
reconnectAttempt: 0,
|
|
226
|
+
theme: v,
|
|
227
|
+
hasMoreHistory: !1,
|
|
228
|
+
isLoadingHistory: !1,
|
|
229
|
+
historyCursor: void 0,
|
|
230
|
+
toggle: () => e((n) => ({
|
|
231
|
+
isOpened: !n.isOpened,
|
|
232
|
+
isRendered: !0,
|
|
233
|
+
unreadCount: 0
|
|
234
|
+
})),
|
|
235
|
+
open: () => e(() => ({ isOpened: !0, isRendered: !0, unreadCount: 0 })),
|
|
236
|
+
close: () => e(() => ({ isOpened: !1 })),
|
|
237
|
+
toggleFullscreen: () => e((n) => ({ isFullscreen: !n.isFullscreen })),
|
|
238
|
+
setFullscreen: (n) => e(() => ({ isFullscreen: n })),
|
|
239
|
+
setAllowFullscreen: (n) => e(() => ({ allowFullscreen: n })),
|
|
240
|
+
setTheme: (n) => e((r) => ({ theme: x(r.theme, n) })),
|
|
241
|
+
getTheme: () => t().theme,
|
|
242
|
+
setConnector: (n) => e(() => ({ activeConnector: n })),
|
|
243
|
+
setConnectorStatus: (n) => e(() => ({ connectorStatus: n })),
|
|
244
|
+
setTyping: (n) => e(() => ({ isTyping: n })),
|
|
245
|
+
incrementUnread: () => e((n) => ({ unreadCount: n.unreadCount + 1 })),
|
|
246
|
+
resetUnread: () => e(() => ({ unreadCount: 0 })),
|
|
247
|
+
setReconnectAttempt: (n) => e(() => ({ reconnectAttempt: n })),
|
|
248
|
+
setHasMoreHistory: (n) => e(() => ({ hasMoreHistory: n })),
|
|
249
|
+
setIsLoadingHistory: (n) => e(() => ({ isLoadingHistory: n })),
|
|
250
|
+
setHistoryCursor: (n) => e(() => ({ historyCursor: n })),
|
|
251
|
+
subscribe: (n) => s.subscribe(() => n())
|
|
252
|
+
}));
|
|
253
|
+
class I {
|
|
254
|
+
constructor(t) {
|
|
255
|
+
this._reconnectTimer = null, this._destroyed = !1, this.connector = t;
|
|
256
|
+
}
|
|
257
|
+
async init() {
|
|
258
|
+
this._destroyed = !1, this.connector.onMessage((t) => {
|
|
259
|
+
s.getState().setTyping(!1);
|
|
260
|
+
const n = w.runAfterReceive(t);
|
|
261
|
+
if (n === null) return;
|
|
262
|
+
s.getState().isOpened || s.getState().incrementUnread();
|
|
263
|
+
const r = p.resolve(n.type);
|
|
264
|
+
y.getState().addMessage({ ...n, from: "bot", component: r });
|
|
265
|
+
}), this.connector.onDisconnect?.((t) => {
|
|
266
|
+
s.getState().setConnectorStatus("disconnected"), !this._destroyed && t !== "user" && this._scheduleReconnect(1);
|
|
267
|
+
}), this.connector.onTyping?.((t) => {
|
|
268
|
+
s.getState().setTyping(t);
|
|
269
|
+
}), this.connector.onMessageStatus?.((t, n) => {
|
|
270
|
+
y.getState().updateById(t, { status: n });
|
|
271
|
+
}), s.getState().setConnectorStatus("connecting");
|
|
272
|
+
try {
|
|
273
|
+
await this.connector.connect(), s.getState().setConnectorStatus("connected"), this.connector.loadHistory && await this.loadHistory();
|
|
274
|
+
} catch (t) {
|
|
275
|
+
throw s.getState().setConnectorStatus("error"), t;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
_scheduleReconnect(t) {
|
|
279
|
+
if (this._destroyed || t > 3) {
|
|
280
|
+
s.getState().setConnectorStatus("error"), s.getState().setReconnectAttempt(0);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
s.getState().setReconnectAttempt(t), s.getState().setConnectorStatus("connecting");
|
|
284
|
+
const n = 2e3 * t;
|
|
285
|
+
this._reconnectTimer = setTimeout(async () => {
|
|
286
|
+
if (!this._destroyed)
|
|
287
|
+
try {
|
|
288
|
+
await this.connector.connect(), s.getState().setConnectorStatus("connected"), s.getState().setReconnectAttempt(0);
|
|
289
|
+
} catch {
|
|
290
|
+
this._scheduleReconnect(t + 1);
|
|
291
|
+
}
|
|
292
|
+
}, n);
|
|
293
|
+
}
|
|
294
|
+
async sendFeedback(t, n) {
|
|
295
|
+
await this.connector.sendFeedback?.(t, n);
|
|
296
|
+
}
|
|
297
|
+
async send(t) {
|
|
298
|
+
const n = w.runBeforeSend(t);
|
|
299
|
+
if (n !== null) {
|
|
300
|
+
if (this.connector.addSentToHistory !== !1) {
|
|
301
|
+
const r = p.resolve(n.type);
|
|
302
|
+
y.getState().addMessage({
|
|
303
|
+
...n,
|
|
304
|
+
from: "user",
|
|
305
|
+
component: r,
|
|
306
|
+
status: "sending"
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
await this.connector.sendMessage(n), this.connector.addSentToHistory !== !1 && y.getState().updateById(n.id, { status: "sent" });
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
async sendFile(t, n) {
|
|
313
|
+
await this.connector.sendFile?.(t, n);
|
|
314
|
+
}
|
|
315
|
+
async loadHistory() {
|
|
316
|
+
if (!this.connector.loadHistory) return;
|
|
317
|
+
const { isLoadingHistory: t, historyCursor: n } = s.getState();
|
|
318
|
+
if (!t) {
|
|
319
|
+
s.getState().setIsLoadingHistory(!0);
|
|
320
|
+
try {
|
|
321
|
+
const r = await this.connector.loadHistory(n), o = r.messages.map((h) => ({
|
|
322
|
+
...h,
|
|
323
|
+
from: h.from ?? "bot",
|
|
324
|
+
component: p.resolve(h.type)
|
|
325
|
+
}));
|
|
326
|
+
y.getState().prependMessages(o), s.getState().setHasMoreHistory(r.hasMore), s.getState().setHistoryCursor(r.cursor);
|
|
327
|
+
} finally {
|
|
328
|
+
s.getState().setIsLoadingHistory(!1);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
async destroy() {
|
|
333
|
+
this._destroyed = !0, this._reconnectTimer !== null && (clearTimeout(this._reconnectTimer), this._reconnectTimer = null), s.getState().setReconnectAttempt(0), await this.connector.disconnect(), s.getState().setConnectorStatus("disconnected");
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
const i = /* @__PURE__ */ new Map(), $ = {
|
|
337
|
+
register(e) {
|
|
338
|
+
if (i.has(e.name))
|
|
339
|
+
throw new Error(
|
|
340
|
+
`ConnectorRegistry: connector "${e.name}" is already registered. Call ConnectorRegistry.unregister("${e.name}") first.`
|
|
341
|
+
);
|
|
342
|
+
i.set(e.name, e);
|
|
343
|
+
},
|
|
344
|
+
get(e) {
|
|
345
|
+
const t = i.get(e);
|
|
346
|
+
if (!t)
|
|
347
|
+
throw new Error(
|
|
348
|
+
`ConnectorRegistry: connector "${e}" not found. Available: [${[...i.keys()].join(", ")}]`
|
|
349
|
+
);
|
|
350
|
+
return t;
|
|
351
|
+
},
|
|
352
|
+
has(e) {
|
|
353
|
+
return i.has(e);
|
|
354
|
+
},
|
|
355
|
+
unregister(e) {
|
|
356
|
+
i.delete(e);
|
|
357
|
+
},
|
|
358
|
+
/** For testing only — clears all registered connectors. */
|
|
359
|
+
clear() {
|
|
360
|
+
i.clear();
|
|
361
|
+
},
|
|
362
|
+
list() {
|
|
363
|
+
return [...i.keys()];
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
export {
|
|
367
|
+
I as ChatEngine,
|
|
368
|
+
$ as ConnectorRegistry,
|
|
369
|
+
v as DEFAULT_THEME,
|
|
370
|
+
w as ExtensionRegistry,
|
|
371
|
+
p as MessageTypeRegistry,
|
|
372
|
+
_ as SlashCommandRegistry,
|
|
373
|
+
s as chatStore,
|
|
374
|
+
A as createOutgoingMessage,
|
|
375
|
+
x as mergeTheme,
|
|
376
|
+
y as messageStore,
|
|
377
|
+
E as resolveText,
|
|
378
|
+
k as themeToCSS
|
|
379
|
+
};
|
|
380
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/domain/entities/Message.ts","../src/domain/ports/ISlashCommand.ts","../src/domain/value-objects/Theme.ts","../src/application/registries/SlashCommandRegistry.ts","../src/application/registries/ExtensionRegistry.ts","../src/application/registries/MessageTypeRegistry.ts","../../../node_modules/.pnpm/zustand@5.0.11/node_modules/zustand/esm/vanilla.mjs","../src/application/stores/MessageStore.ts","../src/application/stores/ChatStore.ts","../src/application/ChatEngine.ts","../src/application/registries/ConnectorRegistry.ts"],"sourcesContent":["/**\n * Domain entities for messages.\n * No external dependencies allowed in this file.\n */\n\nexport type MessageSender = \"user\" | \"bot\";\n\n/** Delivery/read status for a message. */\nexport type MessageStatus = \"sending\" | \"sent\" | \"read\";\n\n/** A tappable chip/button sent alongside a bot message. */\nexport interface MessageAction {\n label: string;\n /** Text to send when tapped; falls back to `label` if omitted. */\n value?: string;\n}\n\nexport interface IncomingMessage {\n id: string;\n type: string;\n from?: MessageSender;\n data: Record<string, unknown>;\n timestamp?: number;\n /** Optional quick-reply chips rendered below the message bubble. */\n actions?: MessageAction[];\n}\n\nexport interface OutgoingMessage {\n id: string;\n type: string;\n data: Record<string, unknown>;\n timestamp?: number;\n}\n\n/** Result returned by IConnector.loadHistory(). */\nexport interface HistoryResult {\n messages: IncomingMessage[];\n hasMore: boolean;\n /** Opaque cursor passed back to load the next (older) page. */\n cursor?: string;\n}\n\nexport function createOutgoingMessage(\n text: string,\n type = \"text\"\n): OutgoingMessage {\n return {\n id: `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,\n type,\n data: { text },\n timestamp: Date.now(),\n };\n}\n","/**\n * ISlashCommand — Port definition for slash commands.\n * No external dependencies allowed in this file.\n */\n\nexport interface CommandContext {\n /** Everything typed after the command name (trimmed). */\n args: string;\n}\n\nexport interface ISlashCommand {\n /** Command name without the leading slash, e.g. \"clear\". */\n readonly name: string;\n /**\n * Short description shown in the autocomplete popup.\n * Pass a function for lazy i18n evaluation at render time:\n * `description: () => t(\"commands.clear.description\")`\n */\n readonly description: string | (() => string);\n /**\n * Optional usage hint displayed next to the command name.\n * Shows accepted parameters, e.g. `\"[count]\"` or `\"<text>\"`.\n * Also supports lazy i18n: `() => t(\"commands.clear.usage\")`\n */\n readonly usage?: string | (() => string);\n execute(context: CommandContext): void;\n}\n\n/** Resolve a description or usage value, calling it if it is a function. */\nexport function resolveText(value: string | (() => string) | undefined): string {\n if (value === undefined) return \"\";\n return typeof value === \"function\" ? value() : value;\n}\n","/**\n * Theme value object — pure types, no logic.\n * No external dependencies allowed.\n */\n\nexport type ButtonPosition = \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\";\nexport type ButtonSize = \"small\" | \"medium\" | \"large\";\nexport type SpaceLevel = \"1\" | \"2\" | \"3\" | \"4\" | \"5\";\n\n/** Avatar configuration for bot and user sides. */\nexport interface AvatarConfig {\n /** URL for the bot avatar image. Omit to use the default robot SVG. */\n bot?: string;\n /** URL for the user avatar image. Omit to use the default person SVG. */\n user?: string;\n /** Show bot avatar. Default: true. */\n showBot?: boolean;\n /** Show user avatar. Default: true. */\n showUser?: boolean;\n}\n\nexport interface ThemeColors {\n primary: string;\n secondary: string;\n background: string;\n text: string;\n border: string;\n}\n\nexport interface LayoutConfig {\n width?: string;\n height?: string;\n maxWidth?: string;\n maxHeight?: string;\n horizontalSpace?: SpaceLevel;\n verticalSpace?: SpaceLevel;\n}\n\nexport interface ThemeConfig {\n colors: ThemeColors;\n position: ButtonPosition;\n positionMargin?: SpaceLevel;\n size: ButtonSize;\n layout: LayoutConfig;\n /** Avatar configuration for bot and user sides. */\n avatar?: AvatarConfig;\n /** Show delivery/read status ticks on user messages. Default: false. */\n showMessageStatus?: boolean;\n}\n\nexport const DEFAULT_THEME: ThemeConfig = {\n colors: {\n primary: \"#4f46e5\",\n secondary: \"#6c757d\",\n background: \"#ffffff\",\n text: \"#212529\",\n border: \"#dee2e6\",\n },\n position: \"bottom-right\",\n positionMargin: \"2\",\n size: \"medium\",\n layout: {\n width: \"360px\",\n height: \"520px\",\n maxWidth: \"100%\",\n maxHeight: \"100%\",\n horizontalSpace: \"2\",\n verticalSpace: \"2\",\n },\n};\n\n/** Build CSS variable map from a ThemeConfig. */\nexport function themeToCSS(theme: ThemeConfig): Record<string, string> {\n return {\n \"--chativa-primary-color\": theme.colors.primary,\n \"--chativa-secondary-color\": theme.colors.secondary,\n \"--chativa-background-color\": theme.colors.background,\n \"--chativa-text-color\": theme.colors.text,\n \"--chativa-border-color\": theme.colors.border,\n };\n}\n\n/** Deep merge a partial theme over a base theme. */\nexport function mergeTheme(\n base: ThemeConfig,\n overrides: DeepPartial<ThemeConfig>\n): ThemeConfig {\n return {\n ...base,\n ...overrides,\n colors: { ...base.colors, ...(overrides.colors ?? {}) },\n layout: { ...base.layout, ...(overrides.layout ?? {}) },\n } as ThemeConfig;\n}\n\nexport type DeepPartial<T> = {\n [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];\n};\n","import type { ISlashCommand, CommandContext } from \"../../domain/ports/ISlashCommand\";\n\nconst _commands = new Map<string, ISlashCommand>();\n\n/**\n * Registry for slash commands.\n * Commands are registered by extensions or app code and executed from ChatInput.\n * Built-in commands (e.g. /clear) are registered at the UI layer so they can\n * use the UI's i18n functions for translated descriptions.\n */\nexport const SlashCommandRegistry = {\n register(command: ISlashCommand): void {\n _commands.set(command.name, command);\n },\n\n unregister(name: string): void {\n _commands.delete(name);\n },\n\n get(name: string): ISlashCommand | undefined {\n return _commands.get(name);\n },\n\n list(): ISlashCommand[] {\n return Array.from(_commands.values());\n },\n\n execute(name: string, args: string): boolean {\n const cmd = _commands.get(name);\n if (!cmd) return false;\n const context: CommandContext = { args };\n cmd.execute(context);\n return true;\n },\n\n has(name: string): boolean {\n return _commands.has(name);\n },\n\n /** Reset all commands — for use in tests only. */\n clear(): void {\n _commands.clear();\n },\n};\n","import type { IExtension, ExtensionContext, MessageTransformer } from \"../../domain/ports/IExtension\";\nimport type { IncomingMessage, OutgoingMessage } from \"../../domain/entities/Message\";\nimport { SlashCommandRegistry } from \"./SlashCommandRegistry\";\n\n/** Internal pipeline storage. */\ninterface Pipeline {\n beforeSend: MessageTransformer<OutgoingMessage>[];\n afterReceive: MessageTransformer<IncomingMessage>[];\n onOpen: (() => void)[];\n onClose: (() => void)[];\n}\n\nconst extensions = new Map<string, IExtension>();\n\nconst pipeline: Pipeline = {\n beforeSend: [],\n afterReceive: [],\n onOpen: [],\n onClose: [],\n};\n\n/** Build an ExtensionContext scoped to a single extension. */\nfunction createContext(): ExtensionContext {\n return {\n onBeforeSend(handler) {\n pipeline.beforeSend.push(handler);\n },\n onAfterReceive(handler) {\n pipeline.afterReceive.push(handler);\n },\n onWidgetOpen(handler) {\n pipeline.onOpen.push(handler);\n },\n onWidgetClose(handler) {\n pipeline.onClose.push(handler);\n },\n registerCommand(command) {\n SlashCommandRegistry.register(command);\n },\n };\n}\n\nexport const ExtensionRegistry = {\n install(extension: IExtension): void {\n if (extensions.has(extension.name)) {\n throw new Error(\n `ExtensionRegistry: extension \"${extension.name}\" is already installed.`\n );\n }\n extensions.set(extension.name, extension);\n extension.install(createContext());\n },\n\n uninstall(name: string): void {\n const ext = extensions.get(name);\n if (!ext) return;\n ext.uninstall?.();\n extensions.delete(name);\n },\n\n /** Run the outgoing message through all beforeSend transformers. Returns null if blocked. */\n runBeforeSend(message: OutgoingMessage): OutgoingMessage | null {\n let msg: OutgoingMessage | null = message;\n for (const handler of pipeline.beforeSend) {\n if (msg === null) return null;\n msg = handler(msg);\n }\n return msg;\n },\n\n /** Run the incoming message through all afterReceive transformers. Returns null if blocked. */\n runAfterReceive(message: IncomingMessage): IncomingMessage | null {\n let msg: IncomingMessage | null = message;\n for (const handler of pipeline.afterReceive) {\n if (msg === null) return null;\n msg = handler(msg);\n }\n return msg;\n },\n\n notifyOpen(): void {\n pipeline.onOpen.forEach((h) => h());\n },\n\n notifyClose(): void {\n pipeline.onClose.forEach((h) => h());\n },\n\n has(name: string): boolean {\n return extensions.has(name);\n },\n\n list(): string[] {\n return [...extensions.keys()];\n },\n\n /** For testing only. */\n clear(): void {\n extensions.clear();\n pipeline.beforeSend = [];\n pipeline.afterReceive = [];\n pipeline.onOpen = [];\n pipeline.onClose = [];\n },\n};\n","/**\n * MessageTypeRegistry — maps message type strings to LitElement component constructors.\n *\n * Application layer: imports only from domain.\n */\n\ntype ComponentConstructor = typeof HTMLElement;\n\nlet fallbackComponent: ComponentConstructor | null = null;\n\nconst registry = new Map<string, ComponentConstructor>();\n\nexport const MessageTypeRegistry = {\n /**\n * Register a component constructor for a message type.\n * @param type The message type key (e.g. \"text\", \"card\", \"image\")\n * @param component The LitElement class to render for this type\n */\n register(type: string, component: ComponentConstructor): void {\n registry.set(type, component);\n },\n\n /**\n * Resolve a component for a given type.\n * Falls back to the registered fallback component (DefaultTextMessage).\n */\n resolve(type: string): ComponentConstructor {\n const component = registry.get(type);\n if (component) return component;\n if (fallbackComponent) return fallbackComponent;\n throw new Error(\n `MessageTypeRegistry: no component for type \"${type}\" and no fallback registered.`\n );\n },\n\n /**\n * Register the fallback component shown when no type match is found.\n */\n setFallback(component: ComponentConstructor): void {\n fallbackComponent = component;\n },\n\n has(type: string): boolean {\n return registry.has(type);\n },\n\n unregister(type: string): void {\n registry.delete(type);\n },\n\n /** For testing only. */\n clear(): void {\n registry.clear();\n fallbackComponent = null;\n },\n\n list(): string[] {\n return [...registry.keys()];\n },\n};\n","const createStoreImpl = (createState) => {\n let state;\n const listeners = /* @__PURE__ */ new Set();\n const setState = (partial, replace) => {\n const nextState = typeof partial === \"function\" ? partial(state) : partial;\n if (!Object.is(nextState, state)) {\n const previousState = state;\n state = (replace != null ? replace : typeof nextState !== \"object\" || nextState === null) ? nextState : Object.assign({}, state, nextState);\n listeners.forEach((listener) => listener(state, previousState));\n }\n };\n const getState = () => state;\n const getInitialState = () => initialState;\n const subscribe = (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n };\n const api = { setState, getState, getInitialState, subscribe };\n const initialState = state = createState(setState, getState, api);\n return api;\n};\nconst createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl);\n\nexport { createStore };\n","import { createStore } from \"zustand/vanilla\";\nimport type { IncomingMessage, MessageStatus } from \"../../domain/entities/Message\";\n\nexport interface StoredMessage extends IncomingMessage {\n component?: typeof HTMLElement;\n /** Delivery/read status (user messages only). */\n status?: MessageStatus;\n}\n\nexport interface MessageStoreState {\n messages: StoredMessage[];\n addMessage: (msg: StoredMessage) => void;\n prependMessages: (msgs: StoredMessage[]) => void;\n removeById: (id: string) => void;\n updateById: (id: string, patch: Partial<StoredMessage>) => void;\n clear: () => void;\n}\n\n/** Track rendered IDs to prevent duplicates. */\nconst renderedIds = new Set<string>();\n\nconst store = createStore<MessageStoreState>((setState) => ({\n messages: [],\n\n addMessage: (msg) =>\n setState((state) => {\n if (renderedIds.has(msg.id)) return state; // deduplicate\n renderedIds.add(msg.id);\n return { messages: [...state.messages, msg] };\n }),\n\n prependMessages: (msgs) =>\n setState((state) => {\n const fresh = msgs.filter((m) => !renderedIds.has(m.id));\n fresh.forEach((m) => renderedIds.add(m.id));\n return { messages: [...fresh, ...state.messages] };\n }),\n\n removeById: (id) =>\n setState((state) => ({\n messages: state.messages.filter((m) => m.id !== id),\n })),\n\n updateById: (id, patch) =>\n setState((state) => ({\n messages: state.messages.map((m) =>\n m.id === id ? { ...m, ...patch } : m\n ),\n })),\n\n clear: () => {\n renderedIds.clear();\n setState(() => ({ messages: [] }));\n },\n}));\n\nexport default store;\n","import { createStore } from \"zustand/vanilla\";\nimport { DEFAULT_THEME, mergeTheme, type ThemeConfig } from \"../../domain/value-objects/Theme\";\nimport type { DeepPartial } from \"../../domain/value-objects/Theme\";\n\n// Re-export for backwards compatibility\nexport type { ThemeConfig };\n\nexport type ConnectorStatus = \"idle\" | \"connecting\" | \"connected\" | \"error\" | \"disconnected\";\n\nexport interface ChatStoreState {\n isOpened: boolean;\n isRendered: boolean;\n isFullscreen: boolean;\n /** When false the fullscreen toggle button in the header is hidden. */\n allowFullscreen: boolean;\n activeConnector: string;\n connectorStatus: ConnectorStatus;\n /** True while the remote peer (bot) is composing a reply. */\n isTyping: boolean;\n /** Number of unread messages received while the chat was closed. */\n unreadCount: number;\n /** Current auto-reconnect attempt (0 = not reconnecting). */\n reconnectAttempt: number;\n theme: ThemeConfig;\n /** Whether the connector has older messages to load. */\n hasMoreHistory: boolean;\n /** True while a history page is being fetched. */\n isLoadingHistory: boolean;\n /** Opaque cursor for the next history page. */\n historyCursor?: string;\n\n toggle: () => void;\n open: () => void;\n close: () => void;\n toggleFullscreen: () => void;\n setFullscreen: (v: boolean) => void;\n setAllowFullscreen: (v: boolean) => void;\n\n setTheme: (theme: DeepPartial<ThemeConfig>) => void;\n getTheme: () => ThemeConfig;\n setConnector: (name: string) => void;\n setConnectorStatus: (status: ConnectorStatus) => void;\n setTyping: (v: boolean) => void;\n incrementUnread: () => void;\n resetUnread: () => void;\n setReconnectAttempt: (n: number) => void;\n setHasMoreHistory: (v: boolean) => void;\n setIsLoadingHistory: (v: boolean) => void;\n setHistoryCursor: (cursor: string | undefined) => void;\n subscribe: (cb: () => void) => () => void;\n}\n\n// Re-export for downstream consumers\nexport type { DeepPartial };\n\nconst store = createStore<ChatStoreState>((setState, getState) => ({\n isOpened: false,\n isRendered: false,\n isFullscreen: false,\n allowFullscreen: true,\n activeConnector: \"dummy\",\n connectorStatus: \"idle\",\n isTyping: false,\n unreadCount: 0,\n reconnectAttempt: 0,\n theme: DEFAULT_THEME,\n hasMoreHistory: false,\n isLoadingHistory: false,\n historyCursor: undefined,\n\n toggle: () =>\n setState((s) => ({\n isOpened: !s.isOpened,\n isRendered: true,\n unreadCount: 0,\n })),\n\n open: () => setState(() => ({ isOpened: true, isRendered: true, unreadCount: 0 })),\n close: () => setState(() => ({ isOpened: false })),\n\n toggleFullscreen: () =>\n setState((s) => ({ isFullscreen: !s.isFullscreen })),\n\n setFullscreen: (v: boolean) => setState(() => ({ isFullscreen: v })),\n\n setAllowFullscreen: (v: boolean) => setState(() => ({ allowFullscreen: v })),\n\n setTheme: (overrides) =>\n setState((s) => ({ theme: mergeTheme(s.theme, overrides) })),\n\n getTheme: () => getState().theme,\n\n setConnector: (name: string) => setState(() => ({ activeConnector: name })),\n\n setConnectorStatus: (status: ConnectorStatus) =>\n setState(() => ({ connectorStatus: status })),\n\n setTyping: (v: boolean) => setState(() => ({ isTyping: v })),\n\n incrementUnread: () => setState((s) => ({ unreadCount: s.unreadCount + 1 })),\n resetUnread: () => setState(() => ({ unreadCount: 0 })),\n setReconnectAttempt: (n: number) => setState(() => ({ reconnectAttempt: n })),\n setHasMoreHistory: (v: boolean) => setState(() => ({ hasMoreHistory: v })),\n setIsLoadingHistory: (v: boolean) => setState(() => ({ isLoadingHistory: v })),\n setHistoryCursor: (cursor: string | undefined) => setState(() => ({ historyCursor: cursor })),\n\n subscribe: (cb: () => void): (() => void) =>\n store.subscribe(() => cb()),\n}));\n\nexport default store;\n","import type { IConnector, FeedbackType } from \"../domain/ports/IConnector\";\nimport type { OutgoingMessage } from \"../domain/entities/Message\";\nimport { ExtensionRegistry } from \"./registries/ExtensionRegistry\";\nimport { MessageTypeRegistry } from \"./registries/MessageTypeRegistry\";\nimport messageStore from \"./stores/MessageStore\";\nimport chatStore from \"./stores/ChatStore\";\n\nexport class ChatEngine {\n private connector: IConnector;\n private _reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private _destroyed = false;\n\n constructor(connector: IConnector) {\n this.connector = connector;\n }\n\n async init(): Promise<void> {\n this._destroyed = false;\n\n this.connector.onMessage((msg) => {\n chatStore.getState().setTyping(false);\n\n const transformed = ExtensionRegistry.runAfterReceive(msg);\n if (transformed === null) return; // extension blocked it\n\n // Increment unread count when chat is closed\n if (!chatStore.getState().isOpened) {\n chatStore.getState().incrementUnread();\n }\n\n const Component = MessageTypeRegistry.resolve(transformed.type);\n messageStore.getState().addMessage({ ...transformed, from: \"bot\", component: Component });\n });\n\n this.connector.onDisconnect?.((reason) => {\n chatStore.getState().setConnectorStatus(\"disconnected\");\n // Auto-reconnect unless destroyed or user-initiated disconnect\n if (!this._destroyed && reason !== \"user\") {\n this._scheduleReconnect(1);\n }\n });\n\n this.connector.onTyping?.((isTyping) => {\n chatStore.getState().setTyping(isTyping);\n });\n\n this.connector.onMessageStatus?.((messageId, status) => {\n messageStore.getState().updateById(messageId, { status });\n });\n\n chatStore.getState().setConnectorStatus(\"connecting\");\n try {\n await this.connector.connect();\n chatStore.getState().setConnectorStatus(\"connected\");\n // Load initial history if supported\n if (this.connector.loadHistory) {\n await this.loadHistory();\n }\n } catch (err) {\n chatStore.getState().setConnectorStatus(\"error\");\n throw err;\n }\n }\n\n private _scheduleReconnect(attempt: number): void {\n if (this._destroyed || attempt > 3) {\n chatStore.getState().setConnectorStatus(\"error\");\n chatStore.getState().setReconnectAttempt(0);\n return;\n }\n\n chatStore.getState().setReconnectAttempt(attempt);\n chatStore.getState().setConnectorStatus(\"connecting\");\n\n const delay = 2000 * attempt; // 2s, 4s, 6s\n this._reconnectTimer = setTimeout(async () => {\n if (this._destroyed) return;\n try {\n await this.connector.connect();\n chatStore.getState().setConnectorStatus(\"connected\");\n chatStore.getState().setReconnectAttempt(0);\n } catch {\n this._scheduleReconnect(attempt + 1);\n }\n }, delay);\n }\n\n async sendFeedback(messageId: string, feedback: FeedbackType): Promise<void> {\n await this.connector.sendFeedback?.(messageId, feedback);\n }\n\n async send(message: OutgoingMessage): Promise<void> {\n const transformed = ExtensionRegistry.runBeforeSend(message);\n if (transformed === null) return; // extension blocked it\n\n if (this.connector.addSentToHistory !== false) {\n const Component = MessageTypeRegistry.resolve(transformed.type);\n messageStore.getState().addMessage({\n ...transformed,\n from: \"user\",\n component: Component,\n status: \"sending\",\n });\n }\n\n await this.connector.sendMessage(transformed);\n\n if (this.connector.addSentToHistory !== false) {\n messageStore.getState().updateById(transformed.id, { status: \"sent\" });\n }\n }\n\n async sendFile(file: File, metadata?: Record<string, unknown>): Promise<void> {\n await this.connector.sendFile?.(file, metadata);\n }\n\n async loadHistory(): Promise<void> {\n if (!this.connector.loadHistory) return;\n const { isLoadingHistory, historyCursor } = chatStore.getState();\n if (isLoadingHistory) return;\n\n chatStore.getState().setIsLoadingHistory(true);\n try {\n const result = await this.connector.loadHistory(historyCursor);\n const msgs = result.messages.map((m) => ({\n ...m,\n from: (m.from ?? \"bot\") as \"bot\" | \"user\",\n component: MessageTypeRegistry.resolve(m.type),\n }));\n messageStore.getState().prependMessages(msgs);\n chatStore.getState().setHasMoreHistory(result.hasMore);\n chatStore.getState().setHistoryCursor(result.cursor);\n } finally {\n chatStore.getState().setIsLoadingHistory(false);\n }\n }\n\n async destroy(): Promise<void> {\n this._destroyed = true;\n if (this._reconnectTimer !== null) {\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = null;\n }\n chatStore.getState().setReconnectAttempt(0);\n await this.connector.disconnect();\n chatStore.getState().setConnectorStatus(\"disconnected\");\n }\n}\n","import type { IConnector } from \"../../domain/ports/IConnector\";\n\nconst registry = new Map<string, IConnector>();\n\nexport const ConnectorRegistry = {\n register(connector: IConnector): void {\n if (registry.has(connector.name)) {\n throw new Error(\n `ConnectorRegistry: connector \"${connector.name}\" is already registered. ` +\n `Call ConnectorRegistry.unregister(\"${connector.name}\") first.`\n );\n }\n registry.set(connector.name, connector);\n },\n\n get(name: string): IConnector {\n const connector = registry.get(name);\n if (!connector) {\n throw new Error(\n `ConnectorRegistry: connector \"${name}\" not found. ` +\n `Available: [${[...registry.keys()].join(\", \")}]`\n );\n }\n return connector;\n },\n\n has(name: string): boolean {\n return registry.has(name);\n },\n\n unregister(name: string): void {\n registry.delete(name);\n },\n\n /** For testing only — clears all registered connectors. */\n clear(): void {\n registry.clear();\n },\n\n list(): string[] {\n return [...registry.keys()];\n },\n};\n"],"names":["createOutgoingMessage","text","type","resolveText","value","DEFAULT_THEME","themeToCSS","theme","mergeTheme","base","overrides","_commands","SlashCommandRegistry","command","name","args","cmd","context","extensions","pipeline","createContext","handler","ExtensionRegistry","extension","ext","message","msg","h","fallbackComponent","registry","MessageTypeRegistry","component","createStoreImpl","createState","state","listeners","setState","partial","replace","nextState","previousState","listener","getState","api","initialState","createStore","renderedIds","store","msgs","fresh","m","id","patch","s","v","status","cursor","cb","ChatEngine","connector","chatStore","transformed","Component","messageStore","reason","isTyping","messageId","err","attempt","delay","feedback","file","metadata","isLoadingHistory","historyCursor","result","ConnectorRegistry"],"mappings":"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","x_google_ignoreList":[6]}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chativa/core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Chativa core — domain interfaces, registries, stores and chat engine (framework-agnostic).",
|
|
5
|
+
"author": "Hamza Agar",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.cjs",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.cjs",
|
|
15
|
+
"types": "./dist/index.d.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"chativa",
|
|
23
|
+
"chat",
|
|
24
|
+
"connector",
|
|
25
|
+
"adapter",
|
|
26
|
+
"bot"
|
|
27
|
+
],
|
|
28
|
+
"homepage": "https://github.com/AimTune/chativa#readme",
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/AimTune/chativa/issues"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/AimTune/chativa.git"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"zustand": "^5.0.6"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
41
|
+
"jsdom": "^28.1.0",
|
|
42
|
+
"typescript": "~5.8.3",
|
|
43
|
+
"vite": "^7.0.3",
|
|
44
|
+
"vite-plugin-dts": "^4.5.4",
|
|
45
|
+
"vitest": "^4.0.18"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "vite build",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"typecheck": "tsc --noEmit"
|
|
51
|
+
}
|
|
52
|
+
}
|