@cognigy/chat-components-vue 0.1.0

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.
Files changed (81) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +178 -0
  3. package/dist/assets/svg/ArrowBackIcon.vue.d.ts +9 -0
  4. package/dist/assets/svg/AudioPauseIcon.vue.d.ts +2 -0
  5. package/dist/assets/svg/AudioPlayIcon.vue.d.ts +2 -0
  6. package/dist/assets/svg/CloseIcon.vue.d.ts +2 -0
  7. package/dist/assets/svg/DownloadIcon.vue.d.ts +2 -0
  8. package/dist/assets/svg/VideoPlayIcon.vue.d.ts +9 -0
  9. package/dist/assets/svg/index.d.ts +7 -0
  10. package/dist/chat-components-vue.css +1 -0
  11. package/dist/chat-components-vue.js +9858 -0
  12. package/dist/components/Message.vue.d.ts +11 -0
  13. package/dist/components/common/ActionButton.vue.d.ts +59 -0
  14. package/dist/components/common/ActionButtons.vue.d.ts +36 -0
  15. package/dist/components/common/ChatBubble.vue.d.ts +22 -0
  16. package/dist/components/common/ChatEvent.vue.d.ts +20 -0
  17. package/dist/components/common/LinkIcon.vue.d.ts +2 -0
  18. package/dist/components/common/TypingIndicator.vue.d.ts +21 -0
  19. package/dist/components/common/Typography.vue.d.ts +38 -0
  20. package/dist/components/messages/AdaptiveCard.vue.d.ts +2 -0
  21. package/dist/components/messages/AudioMessage.vue.d.ts +5 -0
  22. package/dist/components/messages/DatePicker.vue.d.ts +2 -0
  23. package/dist/components/messages/FileMessage.vue.d.ts +2 -0
  24. package/dist/components/messages/Gallery.vue.d.ts +2 -0
  25. package/dist/components/messages/GalleryItem.vue.d.ts +7 -0
  26. package/dist/components/messages/ImageMessage.vue.d.ts +5 -0
  27. package/dist/components/messages/List.vue.d.ts +2 -0
  28. package/dist/components/messages/ListItem.vue.d.ts +16 -0
  29. package/dist/components/messages/TextMessage.vue.d.ts +15 -0
  30. package/dist/components/messages/TextWithButtons.vue.d.ts +2 -0
  31. package/dist/components/messages/VideoMessage.vue.d.ts +5 -0
  32. package/dist/composables/useChannelPayload.d.ts +47 -0
  33. package/dist/composables/useCollation.d.ts +47 -0
  34. package/dist/composables/useImageContext.d.ts +13 -0
  35. package/dist/composables/useMessageContext.d.ts +18 -0
  36. package/dist/composables/useSanitize.d.ts +8 -0
  37. package/dist/index.d.ts +33 -0
  38. package/dist/types/index.d.ts +275 -0
  39. package/dist/utils/helpers.d.ts +56 -0
  40. package/dist/utils/matcher.d.ts +20 -0
  41. package/dist/utils/sanitize.d.ts +28 -0
  42. package/dist/utils/theme.d.ts +18 -0
  43. package/package.json +94 -0
  44. package/src/assets/svg/ArrowBackIcon.vue +30 -0
  45. package/src/assets/svg/AudioPauseIcon.vue +20 -0
  46. package/src/assets/svg/AudioPlayIcon.vue +19 -0
  47. package/src/assets/svg/CloseIcon.vue +10 -0
  48. package/src/assets/svg/DownloadIcon.vue +10 -0
  49. package/src/assets/svg/VideoPlayIcon.vue +25 -0
  50. package/src/assets/svg/index.ts +7 -0
  51. package/src/components/Message.vue +152 -0
  52. package/src/components/common/ActionButton.vue +354 -0
  53. package/src/components/common/ActionButtons.vue +170 -0
  54. package/src/components/common/ChatBubble.vue +109 -0
  55. package/src/components/common/ChatEvent.vue +84 -0
  56. package/src/components/common/LinkIcon.vue +34 -0
  57. package/src/components/common/TypingIndicator.vue +202 -0
  58. package/src/components/common/Typography.vue +196 -0
  59. package/src/components/messages/AdaptiveCard.vue +292 -0
  60. package/src/components/messages/AudioMessage.vue +391 -0
  61. package/src/components/messages/DatePicker.vue +135 -0
  62. package/src/components/messages/FileMessage.vue +195 -0
  63. package/src/components/messages/Gallery.vue +296 -0
  64. package/src/components/messages/GalleryItem.vue +214 -0
  65. package/src/components/messages/ImageMessage.vue +368 -0
  66. package/src/components/messages/List.vue +149 -0
  67. package/src/components/messages/ListItem.vue +344 -0
  68. package/src/components/messages/TextMessage.vue +203 -0
  69. package/src/components/messages/TextWithButtons.vue +119 -0
  70. package/src/components/messages/VideoMessage.vue +343 -0
  71. package/src/composables/useChannelPayload.ts +101 -0
  72. package/src/composables/useCollation.ts +163 -0
  73. package/src/composables/useImageContext.ts +27 -0
  74. package/src/composables/useMessageContext.ts +41 -0
  75. package/src/composables/useSanitize.ts +25 -0
  76. package/src/index.ts +71 -0
  77. package/src/types/index.ts +373 -0
  78. package/src/utils/helpers.ts +164 -0
  79. package/src/utils/matcher.ts +283 -0
  80. package/src/utils/sanitize.ts +133 -0
  81. package/src/utils/theme.ts +58 -0
@@ -0,0 +1,163 @@
1
+ /**
2
+ * useCollation composable
3
+ * Collates consecutive bot messages into single messages for streaming output
4
+ *
5
+ * When `collateStreamedOutputs` is enabled in config, consecutive bot messages
6
+ * are grouped together so they appear as a single message with combined text.
7
+ */
8
+
9
+ import { computed, type ComputedRef, toValue, type MaybeRefOrGetter } from 'vue'
10
+ import type { IMessage } from '@cognigy/socket-client'
11
+ import type { ChatConfig } from '../types'
12
+
13
+ /**
14
+ * A message that may have been collated from multiple bot messages.
15
+ * When collated, text becomes an array and collatedFrom contains the originals.
16
+ */
17
+ export type CollatedMessage = IMessage & {
18
+ /**
19
+ * Original messages that were collated into this one (only present if collated)
20
+ */
21
+ collatedFrom?: IMessage[]
22
+ }
23
+
24
+ export interface UseCollationReturn {
25
+ /**
26
+ * Messages with consecutive bot messages collated
27
+ */
28
+ collatedMessages: ComputedRef<CollatedMessage[]>
29
+
30
+ /**
31
+ * Whether collation is currently enabled
32
+ */
33
+ isCollationEnabled: ComputedRef<boolean>
34
+
35
+ /**
36
+ * Original message count
37
+ */
38
+ originalCount: ComputedRef<number>
39
+
40
+ /**
41
+ * Collated message count
42
+ */
43
+ collatedCount: ComputedRef<number>
44
+ }
45
+
46
+ /**
47
+ * Check if two messages can be collated together
48
+ */
49
+ function canCollate(current: IMessage, previous: IMessage): boolean {
50
+ // Must both be bot messages
51
+ if (current.source !== 'bot' || previous.source !== 'bot') {
52
+ return false
53
+ }
54
+
55
+ // Must both be simple text messages (no rich content)
56
+ if (current.data?._cognigy?._webchat || previous.data?._cognigy?._webchat) {
57
+ return false
58
+ }
59
+
60
+ // Must both have text
61
+ if (!current.text || !previous.text) {
62
+ return false
63
+ }
64
+
65
+ // Don't collate if either has attachments
66
+ if (current.data?.attachments || previous.data?.attachments) {
67
+ return false
68
+ }
69
+
70
+ // Don't collate plugin messages
71
+ if (current.data?._plugin || previous.data?._plugin) {
72
+ return false
73
+ }
74
+
75
+ return true
76
+ }
77
+
78
+ /**
79
+ * Collate consecutive bot messages into combined messages
80
+ *
81
+ * @param messages - Array of messages (can be ref, getter, or plain value)
82
+ * @param config - Chat configuration (can be ref, getter, or plain value)
83
+ * @returns Reactive collated messages
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * const { collatedMessages, isCollationEnabled } = useCollation(messages, config)
88
+ *
89
+ * // Use in template
90
+ * <Message v-for="msg in collatedMessages" :key="msg.traceId" :message="msg" />
91
+ * ```
92
+ */
93
+ export function useCollation(
94
+ messages: MaybeRefOrGetter<IMessage[]>,
95
+ config?: MaybeRefOrGetter<ChatConfig | undefined>
96
+ ): UseCollationReturn {
97
+ const isCollationEnabled = computed(() => {
98
+ const cfg = toValue(config)
99
+ return cfg?.settings?.behavior?.collateStreamedOutputs === true
100
+ })
101
+
102
+ const collatedMessages = computed<CollatedMessage[]>(() => {
103
+ const msgs = toValue(messages)
104
+ const enabled = isCollationEnabled.value
105
+
106
+ if (!enabled || !msgs || msgs.length === 0) {
107
+ return msgs as CollatedMessage[]
108
+ }
109
+
110
+ const result: CollatedMessage[] = []
111
+
112
+ for (let i = 0; i < msgs.length; i++) {
113
+ const current = msgs[i]
114
+
115
+ // Check if we can collate with the last message in result
116
+ if (result.length > 0) {
117
+ const lastCollated = result[result.length - 1]
118
+ const lastOriginal = lastCollated.collatedFrom
119
+ ? lastCollated.collatedFrom[lastCollated.collatedFrom.length - 1]
120
+ : lastCollated
121
+
122
+ if (canCollate(current, lastOriginal)) {
123
+ // Collate: combine texts with newline separator
124
+ const existingText = lastCollated.text ?? ''
125
+ const currentText = current.text ?? ''
126
+
127
+ const collatedFrom = lastCollated.collatedFrom
128
+ ? [...lastCollated.collatedFrom, current]
129
+ : [lastOriginal, current]
130
+
131
+ // Update the last message with combined text (joined by newline)
132
+ result[result.length - 1] = {
133
+ ...lastCollated,
134
+ text: existingText + '\n' + currentText,
135
+ collatedFrom,
136
+ }
137
+ continue
138
+ }
139
+ }
140
+
141
+ // Can't collate, add as new message
142
+ result.push(current as CollatedMessage)
143
+ }
144
+
145
+ return result
146
+ })
147
+
148
+ const originalCount = computed(() => {
149
+ const msgs = toValue(messages)
150
+ return msgs?.length ?? 0
151
+ })
152
+
153
+ const collatedCount = computed(() => {
154
+ return collatedMessages.value.length
155
+ })
156
+
157
+ return {
158
+ collatedMessages,
159
+ isCollationEnabled,
160
+ originalCount,
161
+ collatedCount,
162
+ }
163
+ }
@@ -0,0 +1,27 @@
1
+ import { inject, provide, InjectionKey, ComputedRef } from 'vue'
2
+ import type { IWebchatButton } from '../types'
3
+
4
+ export interface ImageContext {
5
+ onExpand: () => void
6
+ onClose: () => void
7
+ url: string
8
+ altText?: string
9
+ button?: IWebchatButton
10
+ isDownloadable: ComputedRef<boolean>
11
+ }
12
+
13
+ export const ImageContextKey: InjectionKey<ImageContext> = Symbol('ImageContext')
14
+
15
+ export function provideImageContext(context: ImageContext) {
16
+ provide(ImageContextKey, context)
17
+ }
18
+
19
+ export function useImageContext() {
20
+ const context = inject(ImageContextKey)
21
+
22
+ if (!context) {
23
+ throw new Error('useImageContext must be used within an ImageMessage component')
24
+ }
25
+
26
+ return context
27
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Message context composable
3
+ * Provides message context to child components using Vue's provide/inject
4
+ */
5
+
6
+ import { inject, provide, type InjectionKey } from 'vue'
7
+ import type { MessageContext } from '../types'
8
+
9
+ // Injection key for message context
10
+ export const MessageContextKey: InjectionKey<MessageContext> = Symbol('MessageContext')
11
+
12
+ /**
13
+ * Provide message context to child components
14
+ * @param context - Message context to provide
15
+ */
16
+ export function provideMessageContext(context: MessageContext) {
17
+ provide(MessageContextKey, context)
18
+ }
19
+
20
+ /**
21
+ * Use message context in a child component
22
+ * @returns Message context or throws error if not provided
23
+ */
24
+ export function useMessageContext(): MessageContext {
25
+ const context = inject(MessageContextKey)
26
+
27
+ if (!context) {
28
+ console.error('useMessageContext: Context not provided. Make sure component is wrapped in MessageProvider.')
29
+ throw new Error('useMessageContext must be used within a message context provider')
30
+ }
31
+
32
+ return context
33
+ }
34
+
35
+ /**
36
+ * Use message context with optional fallback
37
+ * @returns Message context or undefined if not provided
38
+ */
39
+ export function useMessageContextOptional(): MessageContext | undefined {
40
+ return inject(MessageContextKey, undefined)
41
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * HTML sanitization composable
3
+ * Uses message context for config
4
+ */
5
+
6
+ import { computed } from 'vue'
7
+ import { useMessageContext } from './useMessageContext'
8
+ import { sanitizeHTMLWithConfig } from '../utils/sanitize'
9
+
10
+ export function useSanitize() {
11
+ const { config } = useMessageContext()
12
+
13
+ const isSanitizeEnabled = computed(() => !config?.settings?.layout?.disableHtmlContentSanitization)
14
+ const customAllowedHtmlTags = computed(() => config?.settings?.widgetSettings?.customAllowedHtmlTags)
15
+
16
+ const processHTML = (text: string): string => {
17
+ if (!isSanitizeEnabled.value) return text
18
+ return sanitizeHTMLWithConfig(text, customAllowedHtmlTags.value)
19
+ }
20
+
21
+ return {
22
+ processHTML,
23
+ isSanitizeEnabled,
24
+ }
25
+ }
package/src/index.ts ADDED
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Main entry point for @cognigy/chat-components-vue
3
+ * Exports all public components, composables, and utilities
4
+ */
5
+
6
+ // Main Component
7
+ export { default as Message } from './components/Message.vue'
8
+
9
+ // Common Components
10
+ export { default as TypingIndicator } from './components/common/TypingIndicator.vue'
11
+ export { default as ChatEvent } from './components/common/ChatEvent.vue'
12
+ export { default as Typography } from './components/common/Typography.vue'
13
+ export { default as ActionButtons } from './components/common/ActionButtons.vue'
14
+ export { default as ActionButton } from './components/common/ActionButton.vue'
15
+ export { default as ChatBubble } from './components/common/ChatBubble.vue'
16
+
17
+ // Message Types
18
+ export { default as TextMessage } from './components/messages/TextMessage.vue'
19
+ export { default as ImageMessage } from './components/messages/ImageMessage.vue'
20
+ export { default as VideoMessage } from './components/messages/VideoMessage.vue'
21
+ export { default as AudioMessage } from './components/messages/AudioMessage.vue'
22
+ export { default as TextWithButtons } from './components/messages/TextWithButtons.vue'
23
+ export { default as Gallery } from './components/messages/Gallery.vue'
24
+ export { default as List } from './components/messages/List.vue'
25
+ export { default as FileMessage } from './components/messages/FileMessage.vue'
26
+ export { default as DatePicker } from './components/messages/DatePicker.vue'
27
+ export { default as AdaptiveCard } from './components/messages/AdaptiveCard.vue'
28
+
29
+ // Components to be exported as they are created:
30
+ // export { default as Message } from './components/Message.vue'
31
+
32
+ // Composables
33
+ export { useMessageContext, useMessageContextOptional, provideMessageContext, MessageContextKey } from './composables/useMessageContext'
34
+ export { useSanitize } from './composables/useSanitize'
35
+ export { useImageContext, provideImageContext, ImageContextKey } from './composables/useImageContext'
36
+ export { useChannelPayload, type UseChannelPayloadReturn } from './composables/useChannelPayload'
37
+ export { useCollation, type UseCollationReturn, type CollatedMessage } from './composables/useCollation'
38
+
39
+ // SVG Icons
40
+ export { DownloadIcon, CloseIcon, VideoPlayIcon, AudioPlayIcon, AudioPauseIcon, ArrowBackIcon, LinkIcon } from './assets/svg'
41
+
42
+ // Utilities
43
+ export { match, getChannelPayload } from './utils/matcher'
44
+ export { sanitizeHTMLWithConfig, sanitizeContent } from './utils/sanitize'
45
+ export { configColorsToCssVariables } from './utils/theme'
46
+ export { getWebchatButtonLabel, interpolateString, getRandomId, moveFocusToMessageFocusTarget, replaceUrlsWithHTMLanchorElem, getBackgroundImage, getFileName, getFileExtension, getSizeLabel, isImageAttachment, VALID_IMAGE_MIME_TYPES } from './utils/helpers'
47
+
48
+ // Types
49
+ export type {
50
+ ChatConfig,
51
+ ChatSettings,
52
+ ChatTheme,
53
+ MessageProps,
54
+ MessageSender,
55
+ MessagePlugin,
56
+ MessagePluginOptions,
57
+ MessageContext,
58
+ IMessage,
59
+ IWebchatButton,
60
+ IWebchatQuickReply,
61
+ IWebchatTemplateAttachment,
62
+ IWebchatAttachmentElement,
63
+ IWebchatAudioAttachment,
64
+ IWebchatImageAttachment,
65
+ IWebchatVideoAttachment,
66
+ IUploadFileAttachmentData,
67
+ IDatePickerData,
68
+ IWebchatChannelPayload,
69
+ } from './types'
70
+
71
+ export type { TagVariant } from './components/common/Typography.vue'
@@ -0,0 +1,373 @@
1
+ /**
2
+ * Type definitions for Vue chat components
3
+ * These mirror the React version types but adapted for Vue
4
+ */
5
+
6
+ import type { IMessage } from '@cognigy/socket-client'
7
+ import type { Component } from 'vue'
8
+
9
+ // =============================================================================
10
+ // Extended Socket Client Types
11
+ // =============================================================================
12
+
13
+ /**
14
+ * Extended IMessage with optional runtime properties not in base interface.
15
+ * The socket-client IMessage doesn't include 'id', but it may be present at runtime.
16
+ */
17
+ export interface IMessageWithId extends IMessage {
18
+ id?: string
19
+ }
20
+
21
+ /**
22
+ * Type guard to check if a message has an id property
23
+ */
24
+ export function hasMessageId(message: IMessage): message is IMessageWithId & { id: string } {
25
+ return 'id' in message && typeof (message as IMessageWithId).id === 'string'
26
+ }
27
+
28
+ /**
29
+ * Get message ID with fallback to timestamp-based ID
30
+ */
31
+ export function getMessageId(message: IMessage): string {
32
+ if (hasMessageId(message)) {
33
+ return message.id
34
+ }
35
+ return `message-${message.timestamp ?? Date.now()}`
36
+ }
37
+
38
+ /**
39
+ * Adaptive card payload structure.
40
+ * Used for type narrowing when accessing adaptive card data from message payloads.
41
+ */
42
+ export interface IAdaptiveCardPayload {
43
+ adaptiveCard: unknown
44
+ }
45
+
46
+ /**
47
+ * Type guard to check if a payload contains an adaptive card
48
+ */
49
+ export function isAdaptiveCardPayload(
50
+ payload: unknown
51
+ ): payload is IAdaptiveCardPayload {
52
+ return (
53
+ typeof payload === 'object' &&
54
+ payload !== null &&
55
+ 'adaptiveCard' in payload
56
+ )
57
+ }
58
+
59
+ // =============================================================================
60
+ // Window Interface Extension (for testing)
61
+ // =============================================================================
62
+
63
+ declare global {
64
+ interface Window {
65
+ /** Test-only: Message ID for component testing */
66
+ __TEST_MESSAGE_ID__?: string
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Configuration for the chat components
72
+ * Mirrors IWebchatConfig from React version
73
+ */
74
+ export interface ChatConfig {
75
+ active?: boolean
76
+ URLToken?: string
77
+ initialSessionId?: string
78
+ settings?: ChatSettings
79
+ isConfigLoaded?: boolean
80
+ isTimedOut?: boolean
81
+ }
82
+
83
+ export interface ChatSettings {
84
+ layout?: {
85
+ title?: string
86
+ logoUrl?: string
87
+ botAvatarName?: string
88
+ botLogoUrl?: string
89
+ agentAvatarName?: string
90
+ agentLogoUrl?: string
91
+ disableHtmlContentSanitization?: boolean
92
+ enableGenericHTMLStyling?: boolean
93
+ disableBotOutputBorder?: boolean
94
+ botOutputMaxWidthPercentage?: number
95
+ disableUrlButtonSanitization?: boolean
96
+ dynamicImageAspectRatio?: boolean
97
+ showEngagementInChat?: boolean
98
+ }
99
+ colors?: {
100
+ // Primary action colors
101
+ primaryColor?: string
102
+ primaryColorHover?: string
103
+ primaryColorFocus?: string
104
+ primaryColorDisabled?: string
105
+ primaryContrastColor?: string
106
+ secondaryColor?: string
107
+
108
+ // Message bubble colors
109
+ botMessageColor?: string
110
+ botMessageContrastColor?: string
111
+ userMessageColor?: string
112
+ userMessageContrastColor?: string
113
+ agentMessageColor?: string
114
+ agentMessageContrastColor?: string
115
+
116
+ // Message bubble borders
117
+ borderBotMessage?: string
118
+ borderUserMessage?: string
119
+ borderAgentMessage?: string
120
+
121
+ // Link color
122
+ textLinkColor?: string
123
+ }
124
+ behavior?: {
125
+ renderMarkdown?: boolean
126
+ enableTypingIndicator?: boolean
127
+ messageDelay?: number
128
+ collateStreamedOutputs?: boolean
129
+ focusInputAfterPostback?: boolean
130
+ }
131
+ widgetSettings?: {
132
+ enableDefaultPreview?: boolean
133
+ enableStrictMessengerSync?: boolean
134
+ customAllowedHtmlTags?: string[]
135
+ enableAutoFocus?: boolean
136
+ disableRenderURLsAsLinks?: boolean
137
+ disableTextInputSanitization?: boolean
138
+ sourceDirectionMapping?: {
139
+ user?: 'incoming' | 'outgoing'
140
+ bot?: 'incoming' | 'outgoing'
141
+ agent?: 'incoming' | 'outgoing'
142
+ }
143
+ }
144
+ customTranslations?: {
145
+ ariaLabels?: {
146
+ messageHeader?: {
147
+ user?: string
148
+ bot?: string
149
+ timestamp?: string
150
+ }
151
+ opensInNewTab?: string
152
+ actionButtonPositionText?: string
153
+ viewImageInFullsize?: string
154
+ fullSizeImageViewerTitle?: string
155
+ downloadFullsizeImage?: string
156
+ closeFullsizeImageModal?: string
157
+ playAudio?: string
158
+ pauseAudio?: string
159
+ audioPlaybackProgress?: string
160
+ audioTimeRemaining?: string
161
+ downloadTranscript?: string
162
+ playVideo?: string
163
+ slide?: string
164
+ // Extensibility: consumers can add custom aria labels with any key
165
+ [key: string]: any
166
+ }
167
+ // Extensibility: consumers can add custom translation sections
168
+ [key: string]: any
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Theme configuration
174
+ */
175
+ export interface ChatTheme {
176
+ primaryColor?: string
177
+ primaryColorHover?: string
178
+ secondaryColor?: string
179
+ backgroundBotMessage?: string
180
+ backgroundUserMessage?: string
181
+ textDark?: string
182
+ textLight?: string
183
+ fontFamily?: string
184
+ }
185
+
186
+ /**
187
+ * Message sender callback type.
188
+ * The data parameter uses Record<string, any> because it passes through
189
+ * unchanged to @cognigy/socket-client - shape is determined by backend.
190
+ */
191
+ export type MessageSender = (
192
+ text?: string,
193
+ data?: Record<string, any> | null,
194
+ options?: Partial<SendMessageOptions>
195
+ ) => void
196
+
197
+ export interface SendMessageOptions {
198
+ label: string
199
+ collate: boolean
200
+ }
201
+
202
+ // =============================================================================
203
+ // Component Prop Types
204
+ // =============================================================================
205
+
206
+ /**
207
+ * Custom icon for action buttons.
208
+ * Can be a Vue component or a string identifier.
209
+ */
210
+ export type CustomIcon = Component | string
211
+
212
+ /**
213
+ * Analytics event data passed to analytics callbacks.
214
+ * Shape varies by event type, so we use Record<string, any> for flexibility.
215
+ */
216
+ export type AnalyticsEventData = Record<string, any>
217
+
218
+ /**
219
+ * Callback for emitting analytics events from components.
220
+ */
221
+ export type AnalyticsEventCallback = (event: string, data?: AnalyticsEventData) => void
222
+
223
+ /**
224
+ * Props for the Message component
225
+ */
226
+ export interface MessageProps {
227
+ message: IMessage
228
+ action?: MessageSender
229
+ config?: ChatConfig
230
+ theme?: ChatTheme
231
+ prevMessage?: IMessage
232
+ disableHeader?: boolean
233
+ plugins?: MessagePlugin[]
234
+ onEmitAnalytics?: (event: string, payload?: unknown) => void
235
+ }
236
+
237
+ /**
238
+ * Options for match rules and plugins
239
+ */
240
+ export interface MatchRuleOptions {
241
+ /** If true, continue matching after this rule (allows multiple components) */
242
+ passthrough?: boolean
243
+ /** Render component in fullscreen mode */
244
+ fullscreen?: boolean
245
+ /** Render component at full width */
246
+ fullwidth?: boolean
247
+ }
248
+
249
+ /**
250
+ * Internal match rule used by the matcher system.
251
+ * These define which message types map to which internal components.
252
+ * Components are resolved by name lookup in Message.vue.
253
+ */
254
+ export interface MatchRule {
255
+ /** Unique name identifying this rule (maps to component in Message.vue) */
256
+ name: string
257
+ /** Function to determine if this rule matches a message */
258
+ match: (message: IMessage, config?: ChatConfig) => boolean
259
+ /** Optional rule behavior settings */
260
+ options?: MatchRuleOptions
261
+ }
262
+
263
+ /**
264
+ * External message plugin for custom message types.
265
+ * Users provide these to handle custom message formats.
266
+ */
267
+ export interface MessagePlugin extends MatchRule {
268
+ /** Vue component to render for matched messages (required for external plugins) */
269
+ component: Component
270
+ }
271
+
272
+ /**
273
+ * Union type for matcher results - can be internal rule or external plugin
274
+ */
275
+ export type MatchResult = MatchRule | MessagePlugin
276
+
277
+ /**
278
+ * Type guard to check if a match result is an external plugin with a component
279
+ */
280
+ export function isMessagePlugin(rule: MatchResult): rule is MessagePlugin {
281
+ return 'component' in rule && isValidComponent(rule.component)
282
+ }
283
+
284
+ /**
285
+ * Check if a value is a valid Vue component (not null/undefined/empty)
286
+ */
287
+ export function isValidComponent(component: unknown): component is Component {
288
+ if (!component) return false
289
+ if (typeof component === 'function') return true
290
+ if (typeof component === 'object' && Object.keys(component).length > 0) return true
291
+ return false
292
+ }
293
+
294
+ /** @deprecated Use MatchRuleOptions instead */
295
+ export type MessagePluginOptions = MatchRuleOptions
296
+
297
+ /**
298
+ * Context provided to message components
299
+ */
300
+ export interface MessageContext {
301
+ message: IMessage
302
+ config?: ChatConfig
303
+ theme?: ChatTheme
304
+ action?: MessageSender
305
+ onEmitAnalytics?: (event: string, payload?: unknown) => void
306
+ }
307
+
308
+ /**
309
+ * DatePicker plugin data interface
310
+ */
311
+ export interface IDatePickerData {
312
+ openPickerButtonText?: string
313
+ submitButtonText?: string
314
+ eventName?: string
315
+ dateFormat?: string
316
+ defaultDate?: string
317
+ minDate?: string
318
+ maxDate?: string
319
+ mode?: 'single' | 'multiple' | 'range'
320
+ enableTime?: boolean
321
+ time_24hr?: boolean
322
+ noCalendar?: boolean
323
+ weekNumbers?: boolean
324
+ locale?: string
325
+ enable_disable?: string[]
326
+ function_enable_disable?: string
327
+ wantDisable?: boolean
328
+ defaultHour?: number
329
+ defaultMinute?: number
330
+ hourIncrement?: number
331
+ minuteIncrement?: number
332
+ }
333
+
334
+ /**
335
+ * Export types from @cognigy/socket-client for convenience
336
+ */
337
+ export type { IMessage } from '@cognigy/socket-client'
338
+ export type {
339
+ IWebchatButton,
340
+ IWebchatQuickReply,
341
+ IWebchatTemplateAttachment,
342
+ IWebchatAttachmentElement,
343
+ IWebchatAudioAttachment,
344
+ IWebchatImageAttachment,
345
+ IWebchatVideoAttachment,
346
+ IUploadFileAttachmentData,
347
+ } from '@cognigy/socket-client'
348
+
349
+ // =============================================================================
350
+ // Webchat Channel Payload Types
351
+ // =============================================================================
352
+
353
+ // Import the types we need for the interface
354
+ import type {
355
+ IWebchatQuickReply as QuickReply,
356
+ IWebchatTemplateAttachment as TemplateAttachment,
357
+ IWebchatAudioAttachment as AudioAttachment,
358
+ IWebchatImageAttachment as ImageAttachment,
359
+ IWebchatVideoAttachment as VideoAttachment,
360
+ } from '@cognigy/socket-client'
361
+
362
+ /**
363
+ * Webchat channel payload structure
364
+ * This is the shape of _webchat, _facebook, or _defaultPreview in message.data._cognigy
365
+ */
366
+ export interface IWebchatChannelPayload {
367
+ message?: {
368
+ text?: string
369
+ quick_replies?: QuickReply[]
370
+ attachment?: TemplateAttachment | AudioAttachment | ImageAttachment | VideoAttachment
371
+ }
372
+ adaptiveCard?: unknown
373
+ }