@aleph-alpha/chat-kit 1.0.0 → 1.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 (118) hide show
  1. package/dist/{CkInput.vue_vue_type_script_setup_true_lang-744ZHsQU.js → CkInput.vue_vue_type_script_setup_true_lang-DpYyyCgU.js} +23 -68
  2. package/dist/components/base/CkHistoryItem/CkHistoryItem.vue.d.ts +3 -4
  3. package/dist/components/base/CkHistoryItem/CkHistoryItem.vue.d.ts.map +1 -1
  4. package/dist/components/base/CkHistoryItem/types.d.ts +3 -2
  5. package/dist/components/base/CkHistoryItem/types.d.ts.map +1 -1
  6. package/dist/components/base/CkPromptStopButton/CkPromptStopButton.vue.d.ts.map +1 -1
  7. package/dist/components/index.js +1 -1
  8. package/dist/index.js +1 -1
  9. package/package.json +5 -5
  10. package/src/components/base/CkAgentMessage/CkAgentMessage.spec.ts +91 -0
  11. package/src/components/base/CkAgentMessage/CkAgentMessage.stories.ts +215 -0
  12. package/src/components/base/CkAgentMessage/CkAgentMessage.vue +25 -0
  13. package/src/components/base/CkAgentMessage/index.ts +2 -0
  14. package/src/components/base/CkAgentMessage/types.ts +11 -0
  15. package/src/components/base/CkConversationLayout/CkConversationLayout.spec.ts +129 -0
  16. package/src/components/base/CkConversationLayout/CkConversationLayout.stories.ts +251 -0
  17. package/src/components/base/CkConversationLayout/CkConversationLayout.vue +206 -0
  18. package/src/components/base/CkConversationLayout/index.ts +2 -0
  19. package/src/components/base/CkConversationLayout/types.ts +18 -0
  20. package/src/components/base/CkCopyMessageActionButton/CkCopyMessageActionButton.spec.ts +89 -0
  21. package/src/components/base/CkCopyMessageActionButton/CkCopyMessageActionButton.stories.ts +70 -0
  22. package/src/components/base/CkCopyMessageActionButton/CkCopyMessageActionButton.vue +110 -0
  23. package/src/components/base/CkCopyMessageActionButton/index.ts +2 -0
  24. package/src/components/base/CkCopyMessageActionButton/types.ts +43 -0
  25. package/src/components/base/CkHistoryItem/CkHistoryItem.spec.ts +42 -0
  26. package/src/components/base/CkHistoryItem/CkHistoryItem.stories.ts +230 -0
  27. package/src/components/base/CkHistoryItem/CkHistoryItem.vue +285 -0
  28. package/src/components/base/CkHistoryItem/activeIndicator.ts +15 -0
  29. package/src/components/base/CkHistoryItem/index.ts +2 -0
  30. package/src/components/base/CkHistoryItem/types.ts +111 -0
  31. package/src/components/base/CkHistoryWrapper/CkHistoryWrapper.spec.ts +26 -0
  32. package/src/components/base/CkHistoryWrapper/CkHistoryWrapper.stories.ts +149 -0
  33. package/src/components/base/CkHistoryWrapper/CkHistoryWrapper.vue +36 -0
  34. package/src/components/base/CkHistoryWrapper/index.ts +2 -0
  35. package/src/components/base/CkHistoryWrapper/types.ts +16 -0
  36. package/src/components/base/CkMarkdownRenderer/CkMarkdownRenderer.spec.ts +348 -0
  37. package/src/components/base/CkMarkdownRenderer/CkMarkdownRenderer.stories.ts +382 -0
  38. package/src/components/base/CkMarkdownRenderer/CkMarkdownRenderer.vue +89 -0
  39. package/src/components/base/CkMarkdownRenderer/defaultMarkdownClasses.ts +19 -0
  40. package/src/components/base/CkMarkdownRenderer/index.ts +5 -0
  41. package/src/components/base/CkMarkdownRenderer/types.ts +53 -0
  42. package/src/components/base/CkMessageActionButton/CkMessageActionButton.spec.ts +40 -0
  43. package/src/components/base/CkMessageActionButton/CkMessageActionButton.stories.ts +56 -0
  44. package/src/components/base/CkMessageActionButton/CkMessageActionButton.vue +23 -0
  45. package/src/components/base/CkMessageActionButton/index.ts +2 -0
  46. package/src/components/base/CkMessageActionButton/types.ts +21 -0
  47. package/src/components/base/CkPromptActionButton/CkPromptActionButton.spec.ts +52 -0
  48. package/src/components/base/CkPromptActionButton/CkPromptActionButton.stories.ts +126 -0
  49. package/src/components/base/CkPromptActionButton/CkPromptActionButton.vue +25 -0
  50. package/src/components/base/CkPromptActionButton/index.ts +2 -0
  51. package/src/components/base/CkPromptActionButton/types.ts +32 -0
  52. package/src/components/base/CkPromptInput/CkPromptInput.spec.ts +181 -0
  53. package/src/components/base/CkPromptInput/CkPromptInput.stories.ts +224 -0
  54. package/src/components/base/CkPromptInput/CkPromptInput.vue +104 -0
  55. package/src/components/base/CkPromptInput/index.ts +2 -0
  56. package/src/components/base/CkPromptInput/types.ts +39 -0
  57. package/src/components/base/CkPromptInputDisclaimer/CkPromptInputDisclaimer.spec.ts +40 -0
  58. package/src/components/base/CkPromptInputDisclaimer/CkPromptInputDisclaimer.stories.ts +103 -0
  59. package/src/components/base/CkPromptInputDisclaimer/CkPromptInputDisclaimer.vue +16 -0
  60. package/src/components/base/CkPromptInputDisclaimer/index.ts +2 -0
  61. package/src/components/base/CkPromptInputDisclaimer/types.ts +17 -0
  62. package/src/components/base/CkPromptStopButton/CkPromptStopButton.spec.ts +64 -0
  63. package/src/components/base/CkPromptStopButton/CkPromptStopButton.stories.ts +81 -0
  64. package/src/components/base/CkPromptStopButton/CkPromptStopButton.vue +27 -0
  65. package/src/components/base/CkPromptStopButton/index.ts +2 -0
  66. package/src/components/base/CkPromptStopButton/types.ts +28 -0
  67. package/src/components/base/CkUserMessage/CkUserMessage.spec.ts +20 -0
  68. package/src/components/base/CkUserMessage/CkUserMessage.stories.ts +112 -0
  69. package/src/components/base/CkUserMessage/CkUserMessage.vue +19 -0
  70. package/src/components/base/CkUserMessage/index.ts +2 -0
  71. package/src/components/base/CkUserMessage/types.ts +14 -0
  72. package/src/components/base/icons/CkSendIcon.vue +22 -0
  73. package/src/components/base/icons/CkStopIcon.vue +18 -0
  74. package/src/components/base/icons/index.ts +2 -0
  75. package/src/components/base/index.ts +21 -0
  76. package/src/components/composed/CkConversation/CkConversation.spec.ts +439 -0
  77. package/src/components/composed/CkConversation/CkConversation.stories.ts +642 -0
  78. package/src/components/composed/CkConversation/CkConversation.vue +146 -0
  79. package/src/components/composed/CkConversation/index.ts +2 -0
  80. package/src/components/composed/CkConversation/types.ts +37 -0
  81. package/src/components/composed/CkHistory/CkHistory.spec.ts +327 -0
  82. package/src/components/composed/CkHistory/CkHistory.stories.ts +373 -0
  83. package/src/components/composed/CkHistory/CkHistory.vue +126 -0
  84. package/src/components/composed/CkHistory/index.ts +2 -0
  85. package/src/components/composed/CkHistory/types.ts +72 -0
  86. package/src/components/composed/CkInput/CkInput.spec.ts +191 -0
  87. package/src/components/composed/CkInput/CkInput.stories.ts +438 -0
  88. package/src/components/composed/CkInput/CkInput.vue +104 -0
  89. package/src/components/composed/CkInput/index.ts +2 -0
  90. package/src/components/composed/CkInput/types.ts +60 -0
  91. package/src/components/composed/index.ts +5 -0
  92. package/src/components/index.ts +34 -0
  93. package/src/composables/index.ts +9 -0
  94. package/src/composables/types.ts +36 -0
  95. package/src/composables/useChat.ts +18 -0
  96. package/src/composables/useConversations.spec.ts +557 -0
  97. package/src/composables/useConversations.ts +321 -0
  98. package/src/handlers/index.ts +9 -0
  99. package/src/handlers/useChatKitLabels.spec.ts +290 -0
  100. package/src/handlers/useChatKitLabels.ts +96 -0
  101. package/src/handlers/useMarkdownParser.spec.ts +112 -0
  102. package/src/handlers/useMarkdownParser.ts +47 -0
  103. package/src/handlers/useMessageHandler.spec.ts +535 -0
  104. package/src/handlers/useMessageHandler.ts +138 -0
  105. package/src/helpers/conversationTree.spec.ts +423 -0
  106. package/src/helpers/conversationTree.ts +117 -0
  107. package/src/helpers/generateId.ts +5 -0
  108. package/src/helpers/index.ts +4 -0
  109. package/src/helpers/secondsToDate.ts +7 -0
  110. package/src/helpers/toError.ts +8 -0
  111. package/src/index.ts +79 -0
  112. package/src/services/ChatService.ts +60 -0
  113. package/src/services/MockChatService.ts +47 -0
  114. package/src/services/index.ts +12 -0
  115. package/src/services/types.ts +92 -0
  116. package/src/test-setup.ts +12 -0
  117. package/src/test-utils/createTextPlugin.ts +9 -0
  118. package/src/types.ts +47 -0
@@ -0,0 +1,642 @@
1
+ import type { BaseMessage } from '../../../types';
2
+ import CkConversation from './CkConversation.vue';
3
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
4
+ import { ref } from 'vue';
5
+
6
+ const now = new Date();
7
+
8
+ const markdownContent = [
9
+ 'Based on a review of internal documents, here are the **key lessons learned** and best practices from the *Digital Transformation Phase 1* project, broken down by category.',
10
+ '',
11
+ '## Key Lessons Learned',
12
+ '',
13
+ '- **Stakeholder Involvement:** The most critical lesson was the need for early and continuous stakeholder involvement.',
14
+ '- **User Training:** Tailored, hands-on training was found to be far more effective than generic sessions.',
15
+ '- **Centralized Feedback:** Establishing a centralized feedback loop was identified as crucial.',
16
+ '',
17
+ '## Best Practices & Successes',
18
+ '',
19
+ '1. **Phased Rollout:** A major success was the phased rollout of tools and processes.',
20
+ '2. **Cross-team Collaboration:** Regular sync meetings across departments improved alignment.',
21
+ '',
22
+ '> **Note:** For detailed metrics, refer to the project dashboard.',
23
+ '',
24
+ 'Here is a quick summary:',
25
+ '',
26
+ '| Category | Status | Impact |',
27
+ '| --- | --- | --- |',
28
+ '| Stakeholder Engagement | Completed | High |',
29
+ '| Training Programs | In Progress | Medium |',
30
+ '| Feedback Integration | Planned | High |',
31
+ ].join('\n');
32
+
33
+ const sampleMessages: BaseMessage[] = [
34
+ {
35
+ id: '1',
36
+ role: 'user',
37
+ content: 'Hello! Can you help me with something?',
38
+ createdAt: now,
39
+ status: 'completed',
40
+ },
41
+ {
42
+ id: '2',
43
+ role: 'assistant',
44
+ content: 'Of course! I am here to help. What would you like to know?',
45
+ createdAt: now,
46
+ status: 'completed',
47
+ },
48
+ {
49
+ id: '3',
50
+ role: 'user',
51
+ content:
52
+ "Find the lessons learned and best practices from the 'Digital Transformation Phase 1' project, specifically focusing on process optimization and digital tool integration.",
53
+ createdAt: now,
54
+ status: 'completed',
55
+ },
56
+ {
57
+ id: '4',
58
+ role: 'assistant',
59
+ content: markdownContent,
60
+ createdAt: now,
61
+ status: 'completed',
62
+ },
63
+ ];
64
+
65
+ const meta: Meta<typeof CkConversation> = {
66
+ title: 'Composed/CkConversation',
67
+ component: CkConversation,
68
+ tags: ['autodocs'],
69
+ argTypes: {
70
+ messages: {
71
+ control: 'object',
72
+ description: 'Array of messages to render in chronological order.',
73
+ },
74
+ agentAvatarImage: {
75
+ control: 'text',
76
+ description: 'URL of the avatar image shown next to every agent message.',
77
+ },
78
+ agentAvatarFallback: {
79
+ control: 'text',
80
+ description: 'Fallback text/initials when no avatar image is provided.',
81
+ },
82
+ agentLoadingImage: {
83
+ control: 'text',
84
+ description:
85
+ 'Avatar image used while an agent message is being generated.',
86
+ },
87
+ agentMarkdownClasses: {
88
+ control: 'object',
89
+ description:
90
+ 'Per-tag class overrides for markdown rendered inside agent messages.',
91
+ },
92
+ },
93
+ decorators: [
94
+ () => ({
95
+ template:
96
+ '<div style="height: 400px; border: 1px solid #e0e0e0; border-radius: 8px; overflow: hidden;"><story /></div>',
97
+ }),
98
+ ],
99
+ };
100
+
101
+ export default meta;
102
+ type Story = StoryObj<typeof meta>;
103
+
104
+ const defaultTemplateSource = `<script setup lang="ts">
105
+ import { CkConversation } from '@aleph-alpha/chat-kit';
106
+ import type { BaseMessage } from '@aleph-alpha/chat-kit';
107
+
108
+ const messages: BaseMessage[] = [
109
+ { id: '1', role: 'user', content: 'Hello!', createdAt: new Date(), status: 'completed' },
110
+ { id: '2', role: 'assistant', content: 'Hi there!', createdAt: new Date(), status: 'completed' },
111
+ ];
112
+ </script>
113
+
114
+ <template>
115
+ <CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;" />
116
+ </template>`;
117
+
118
+ const withIconTemplateSource = `<script setup lang="ts">
119
+ import { CkConversation } from '@aleph-alpha/chat-kit';
120
+ import type { BaseMessage } from '@aleph-alpha/chat-kit';
121
+
122
+ const messages: BaseMessage[] = [/* ... */];
123
+ </script>
124
+
125
+ <template>
126
+ <CkConversation
127
+ :messages="messages"
128
+ agent-avatar-image="https://ui-avatars.com/api/?name=AI&background=6366f1&color=fff&size=64"
129
+ agent-avatar-fallback="AI"
130
+ style="height: 100%;"
131
+ />
132
+ </template>`;
133
+
134
+ const withCustomAvatarFallbackTemplateSource = `<script setup lang="ts">
135
+ import { CkConversation } from '@aleph-alpha/chat-kit';
136
+ import type { BaseMessage } from '@aleph-alpha/chat-kit';
137
+
138
+ const messages: BaseMessage[] = [/* ... */];
139
+ </script>
140
+
141
+ <template>
142
+ <CkConversation :messages="messages" agent-avatar-fallback="WB" style="height: 100%;" />
143
+ </template>`;
144
+
145
+ const noIconTemplateSource = `<script setup lang="ts">
146
+ import { CkConversation } from '@aleph-alpha/chat-kit';
147
+ import type { BaseMessage } from '@aleph-alpha/chat-kit';
148
+
149
+ const messages: BaseMessage[] = [/* ... */];
150
+ </script>
151
+
152
+ <template>
153
+ <CkConversation :messages="messages" style="height: 100%;" />
154
+ </template>`;
155
+
156
+ const withCustomIconTemplateSource = `<script setup lang="ts">
157
+ import { CkConversation } from '@aleph-alpha/chat-kit';
158
+ import type { BaseMessage } from '@aleph-alpha/chat-kit';
159
+
160
+ const messages: BaseMessage[] = [/* ... */];
161
+ </script>
162
+
163
+ <template>
164
+ <CkConversation :messages="messages" style="height: 100%;">
165
+ <template #agent-message-icon>
166
+ <div class="bg-core-bg-tertiary flex size-8 items-center justify-center rounded-full text-xs">AI</div>
167
+ </template>
168
+ </CkConversation>
169
+ </template>`;
170
+
171
+ const withFooterActionsTemplateSource = `<script setup lang="ts">
172
+ import { CkConversation } from '@aleph-alpha/chat-kit';
173
+ import type { BaseMessage } from '@aleph-alpha/chat-kit';
174
+
175
+ const messages: BaseMessage[] = [/* ... */];
176
+ </script>
177
+
178
+ <template>
179
+ <CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
180
+ <template #agent-message-footer>
181
+ <button>Copy</button>
182
+ <button>Regenerate</button>
183
+ <button>Like</button>
184
+ <button>Dislike</button>
185
+ </template>
186
+ </CkConversation>
187
+ </template>`;
188
+
189
+ const withCustomUserContentTemplateSource = `<script setup lang="ts">
190
+ import { CkConversation } from '@aleph-alpha/chat-kit';
191
+ import type { BaseMessage } from '@aleph-alpha/chat-kit';
192
+
193
+ const messages: BaseMessage[] = [/* ... */];
194
+ </script>
195
+
196
+ <template>
197
+ <CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
198
+ <template #user-message-content="{ message }">
199
+ <strong>{{ message.content }}</strong>
200
+ </template>
201
+ </CkConversation>
202
+ </template>`;
203
+
204
+ const withCustomAgentContentTemplateSource = `<script setup lang="ts">
205
+ import { CkConversation } from '@aleph-alpha/chat-kit';
206
+ import type { BaseMessage } from '@aleph-alpha/chat-kit';
207
+
208
+ const messages: BaseMessage[] = [/* ... */];
209
+ </script>
210
+
211
+ <template>
212
+ <CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
213
+ <template #agent-message-content="{ message }">
214
+ <div class="whitespace-pre-wrap font-mono text-sm">{{ message.content }}</div>
215
+ </template>
216
+ </CkConversation>
217
+ </template>`;
218
+
219
+ const streamingSimulationTemplateSource = `<script setup lang="ts">
220
+ import { CkConversation } from '@aleph-alpha/chat-kit';
221
+ import type { BaseMessage } from '@aleph-alpha/chat-kit';
222
+ import { ref } from 'vue';
223
+
224
+ const messages = ref<BaseMessage[]>([]);
225
+
226
+ function sendAndStream(): void {
227
+ messages.value = [
228
+ ...messages.value,
229
+ { id: '1', role: 'user', content: 'Tell me something.', createdAt: new Date(), status: 'completed' },
230
+ ];
231
+ messages.value = [
232
+ ...messages.value,
233
+ { id: '2', role: 'assistant', content: '', createdAt: new Date(), status: 'sending' },
234
+ ];
235
+ const reply = 'Key lessons learned from the project...';
236
+ let i = 0;
237
+ const interval = setInterval(() => {
238
+ i = Math.min(i + 3, reply.length);
239
+ const last = messages.value.length - 1;
240
+ messages.value[last] = { ...messages.value[last], content: reply.slice(0, i) };
241
+ if (i >= reply.length) clearInterval(interval);
242
+ }, 30);
243
+ }
244
+ </script>
245
+
246
+ <template>
247
+ <CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 400px;" />
248
+ <button @click="sendAndStream">Send + simulate stream</button>
249
+ </template>`;
250
+
251
+ const withCustomMarkdownClassesTemplateSource = `<script setup lang="ts">
252
+ import { CkConversation } from '@aleph-alpha/chat-kit';
253
+ import type { BaseMessage } from '@aleph-alpha/chat-kit';
254
+
255
+ const messages: BaseMessage[] = [/* ... */];
256
+ const customClasses = {
257
+ h2: 'mt-6 mb-3 text-2xl font-bold',
258
+ p: 'my-2 leading-8 text-lg',
259
+ a: 'text-blue-600 underline',
260
+ };
261
+ </script>
262
+
263
+ <template>
264
+ <CkConversation
265
+ :messages="messages"
266
+ :agent-markdown-classes="customClasses"
267
+ agent-avatar-fallback="AI"
268
+ style="height: 100%;"
269
+ />
270
+ </template>`;
271
+
272
+ const withCustomCodeBlockTemplateSource = `<script setup lang="ts">
273
+ import { CkConversation } from '@aleph-alpha/chat-kit';
274
+ import type { BaseMessage } from '@aleph-alpha/chat-kit';
275
+
276
+ const messages: BaseMessage[] = [/* ... */];
277
+ </script>
278
+
279
+ <template>
280
+ <CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
281
+ <template #agent-message-code-block="{ code, language }">
282
+ <div class="rounded-lg bg-black p-4 font-mono text-white">
283
+ <div class="mb-2 text-xs uppercase opacity-60">{{ language }}</div>
284
+ <code>{{ code }}</code>
285
+ </div>
286
+ </template>
287
+ </CkConversation>
288
+ </template>`;
289
+
290
+ const emptyTemplateSource = `<script setup lang="ts">
291
+ import { CkConversation } from '@aleph-alpha/chat-kit';
292
+ </script>
293
+
294
+ <template>
295
+ <CkConversation :messages="[]" style="height: 100%;" />
296
+ </template>`;
297
+
298
+ /**
299
+ * Drop-in conversation UI. Pass `messages` with `role`, `content`, and `status`
300
+ * fields and CkConversation renders user bubbles, assistant markdown, avatars,
301
+ * and a copy button on each reply.
302
+ */
303
+ export const Default: Story = {
304
+ render: () => ({
305
+ components: { CkConversation },
306
+ setup() {
307
+ return { messages: sampleMessages };
308
+ },
309
+ template:
310
+ '<CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;" />',
311
+ }),
312
+ parameters: { docs: { source: { code: defaultTemplateSource } } },
313
+ };
314
+
315
+ /**
316
+ * Uses a remote avatar image (with fallback text). Use when you want a
317
+ * branded agent identity in each reply.
318
+ */
319
+ export const WithIcon: Story = {
320
+ render: () => ({
321
+ components: { CkConversation },
322
+ setup() {
323
+ return { messages: sampleMessages };
324
+ },
325
+ template:
326
+ '<CkConversation :messages="messages" agent-avatar-image="https://ui-avatars.com/api/?name=AI&background=6366f1&color=fff&size=64" agent-avatar-fallback="AI" style="height: 100%;" />',
327
+ }),
328
+ parameters: { docs: { source: { code: withIconTemplateSource } } },
329
+ };
330
+
331
+ /**
332
+ * Text-only avatar via `agent-avatar-fallback`. Use when you have no image
333
+ * and want to display initials instead.
334
+ */
335
+ export const WithCustomAvatarFallback: Story = {
336
+ render: () => ({
337
+ components: { CkConversation },
338
+ setup() {
339
+ return { messages: sampleMessages };
340
+ },
341
+ template:
342
+ '<CkConversation :messages="messages" agent-avatar-fallback="WB" style="height: 100%;" />',
343
+ }),
344
+ parameters: {
345
+ docs: { source: { code: withCustomAvatarFallbackTemplateSource } },
346
+ },
347
+ };
348
+
349
+ /**
350
+ * Hides the avatar completely. Use for minimalist chat surfaces where the
351
+ * assistant identity is conveyed elsewhere (e.g. surrounding header).
352
+ */
353
+ export const NoIcon: Story = {
354
+ render: () => ({
355
+ components: { CkConversation },
356
+ setup() {
357
+ return { messages: sampleMessages };
358
+ },
359
+ template: '<CkConversation :messages="messages" style="height: 100%;" />',
360
+ }),
361
+ parameters: { docs: { source: { code: noIconTemplateSource } } },
362
+ };
363
+
364
+ /**
365
+ * Replaces the default avatar with custom markup through the
366
+ * `#agent-message-icon` slot. Use to inject an emoji, Lottie animation, or
367
+ * your own branded icon.
368
+ */
369
+ export const WithCustomIcon: Story = {
370
+ render: () => ({
371
+ components: { CkConversation },
372
+ setup() {
373
+ return { messages: sampleMessages };
374
+ },
375
+ template: `
376
+ <CkConversation :messages="messages" style="height: 100%;">
377
+ <template #agent-message-icon>
378
+ <div class="bg-core-bg-tertiary flex size-8 items-center justify-center rounded-full text-xs">🤖</div>
379
+ </template>
380
+ </CkConversation>
381
+ `,
382
+ }),
383
+ parameters: { docs: { source: { code: withCustomIconTemplateSource } } },
384
+ };
385
+
386
+ /**
387
+ * Overrides `#agent-message-footer` to add custom action buttons (copy,
388
+ * regenerate, thumbs up/down) next to each assistant reply. Use to add
389
+ * feedback or follow-up actions.
390
+ */
391
+ export const WithFooterActions: Story = {
392
+ render: () => ({
393
+ components: { CkConversation },
394
+ setup() {
395
+ return { messages: sampleMessages };
396
+ },
397
+ template: `
398
+ <CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
399
+ <template #agent-message-footer>
400
+ <button class="p-1 opacity-60 hover:opacity-100">📋</button>
401
+ <button class="p-1 opacity-60 hover:opacity-100">🔄</button>
402
+ <button class="p-1 opacity-60 hover:opacity-100">👍</button>
403
+ <button class="p-1 opacity-60 hover:opacity-100">👎</button>
404
+ </template>
405
+ </CkConversation>
406
+ `,
407
+ }),
408
+ parameters: { docs: { source: { code: withFooterActionsTemplateSource } } },
409
+ };
410
+
411
+ /**
412
+ * Customizes the user message body via `#user-message-content`. Use to apply
413
+ * special typography, mentions, or attachments to user turns.
414
+ */
415
+ export const WithCustomUserContent: Story = {
416
+ render: () => ({
417
+ components: { CkConversation },
418
+ setup() {
419
+ return { messages: sampleMessages };
420
+ },
421
+ template: `
422
+ <CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
423
+ <template #user-message-content="{ message }">
424
+ <strong>{{ message.content }}</strong>
425
+ </template>
426
+ </CkConversation>
427
+ `,
428
+ }),
429
+ parameters: {
430
+ docs: { source: { code: withCustomUserContentTemplateSource } },
431
+ },
432
+ };
433
+
434
+ /**
435
+ * Replaces the default Markdown rendering with a custom layout via
436
+ * `#agent-message-content`. Use when you want raw content display or a
437
+ * different renderer.
438
+ */
439
+ export const WithCustomAgentContent: Story = {
440
+ render: () => ({
441
+ components: { CkConversation },
442
+ setup() {
443
+ return { messages: sampleMessages };
444
+ },
445
+ template: `
446
+ <CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
447
+ <template #agent-message-content="{ message }">
448
+ <div style="white-space: pre-wrap; font-family: monospace; font-size: 13px;">{{ message.content }}</div>
449
+ </template>
450
+ </CkConversation>
451
+ `,
452
+ }),
453
+ parameters: {
454
+ docs: { source: { code: withCustomAgentContentTemplateSource } },
455
+ },
456
+ };
457
+
458
+ /**
459
+ * End-to-end streaming scenario: appending a new user message and then
460
+ * streaming an assistant reply chunk-by-chunk. Use as a reference for wiring
461
+ * up an LLM SDK that emits token deltas.
462
+ */
463
+ export const StreamingSimulation: Story = {
464
+ render: () => ({
465
+ components: { CkConversation },
466
+ setup() {
467
+ const messages = ref<BaseMessage[]>([
468
+ {
469
+ id: '1',
470
+ role: 'user',
471
+ content: 'Hello!',
472
+ createdAt: now,
473
+ status: 'completed',
474
+ },
475
+ {
476
+ id: '2',
477
+ role: 'assistant',
478
+ content: 'Hi there!',
479
+ createdAt: now,
480
+ status: 'completed',
481
+ },
482
+ ]);
483
+
484
+ let streamInterval: ReturnType<typeof setInterval> | null = null;
485
+ let charIndex = 0;
486
+ let nextId = 3;
487
+
488
+ function streamNextChunk(): void {
489
+ if (charIndex >= markdownContent.length) {
490
+ if (streamInterval) clearInterval(streamInterval);
491
+ return;
492
+ }
493
+ // eslint-disable-next-line sonarjs/pseudo-random
494
+ const chunkSize = Math.floor(Math.random() * 5) + 3;
495
+ charIndex = Math.min(charIndex + chunkSize, markdownContent.length);
496
+ const lastIdx = messages.value.length - 1;
497
+ messages.value[lastIdx] = {
498
+ ...messages.value[lastIdx],
499
+ content: markdownContent.slice(0, charIndex),
500
+ };
501
+ }
502
+
503
+ function startStreaming(): void {
504
+ const id = String(nextId++);
505
+ messages.value = [
506
+ ...messages.value,
507
+ {
508
+ id,
509
+ role: 'assistant',
510
+ content: '',
511
+ createdAt: new Date(),
512
+ status: 'sending',
513
+ },
514
+ ];
515
+ charIndex = 0;
516
+ streamInterval = setInterval(streamNextChunk, 30);
517
+ }
518
+
519
+ function simulateStream(): void {
520
+ const id = String(nextId++);
521
+ messages.value = [
522
+ ...messages.value,
523
+ {
524
+ id,
525
+ role: 'user',
526
+ content:
527
+ 'Tell me about Digital Transformation Phase 1 lessons learned.',
528
+ createdAt: new Date(),
529
+ status: 'completed',
530
+ },
531
+ ];
532
+ setTimeout(startStreaming, 500);
533
+ }
534
+
535
+ return { messages, simulateStream };
536
+ },
537
+ template: `
538
+ <div style="display: flex; flex-direction: column; height: 100%;">
539
+ <CkConversation :messages="messages" agent-avatar-fallback="AI" style="flex: 1; min-height: 0;" />
540
+ <div style="padding: 8px; border-top: 1px solid #e0e0e0;">
541
+ <button @click="simulateStream" style="padding: 6px 16px; border-radius: 6px; background: #333; color: white; cursor: pointer;">
542
+ Send message + simulate stream
543
+ </button>
544
+ </div>
545
+ </div>
546
+ `,
547
+ }),
548
+ parameters: { docs: { source: { code: streamingSimulationTemplateSource } } },
549
+ };
550
+
551
+ /**
552
+ * Overrides default Markdown element classes via `agent-markdown-classes`.
553
+ * Use to apply your design-system tokens to assistant replies without writing
554
+ * a custom renderer slot.
555
+ */
556
+ export const WithCustomMarkdownClasses: Story = {
557
+ render: () => ({
558
+ components: { CkConversation },
559
+ setup() {
560
+ return {
561
+ messages: sampleMessages,
562
+ customClasses: {
563
+ h2: 'mt-6 mb-3 text-2xl font-bold',
564
+ p: 'my-2 leading-8 text-lg',
565
+ a: 'text-blue-600 underline',
566
+ },
567
+ };
568
+ },
569
+ template:
570
+ '<CkConversation :messages="messages" :agent-markdown-classes="customClasses" agent-avatar-fallback="AI" style="height: 100%;" />',
571
+ }),
572
+ parameters: {
573
+ docs: { source: { code: withCustomMarkdownClassesTemplateSource } },
574
+ },
575
+ };
576
+
577
+ /**
578
+ * Integrates a custom code-block renderer (Shiki, Prism, etc.) via
579
+ * `#agent-message-code-block`. Use when you want syntax highlighting or
580
+ * inline copy buttons for assistant code.
581
+ */
582
+ export const WithCustomCodeBlock: Story = {
583
+ render: () => ({
584
+ components: { CkConversation },
585
+ setup() {
586
+ const messages: BaseMessage[] = [
587
+ {
588
+ id: '1',
589
+ role: 'user',
590
+ content: 'Show me a code example',
591
+ createdAt: now,
592
+ status: 'completed',
593
+ },
594
+ {
595
+ id: '2',
596
+ role: 'assistant',
597
+ content: [
598
+ 'Here is an example:',
599
+ '',
600
+ '```typescript',
601
+ 'const greeting = "Hello, world!";',
602
+ 'console.log(greeting);',
603
+ '```',
604
+ '',
605
+ 'And another one:',
606
+ '',
607
+ '```python',
608
+ 'def greet(name: str) -> str:',
609
+ ' return f"Hello, {name}!"',
610
+ '```',
611
+ ].join('\n'),
612
+ createdAt: now,
613
+ status: 'completed',
614
+ },
615
+ ];
616
+ return { messages };
617
+ },
618
+ template: `
619
+ <CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
620
+ <template #agent-message-code-block="{ code, language }">
621
+ <div style="background: #1e1e1e; color: #d4d4d4; padding: 16px; border-radius: 8px; font-family: monospace; white-space: pre; margin: 8px 0;">
622
+ <div style="color: #888; font-size: 12px; margin-bottom: 8px; text-transform: uppercase;">{{ language }}</div>
623
+ <code>{{ code }}</code>
624
+ </div>
625
+ </template>
626
+ </CkConversation>
627
+ `,
628
+ }),
629
+ parameters: { docs: { source: { code: withCustomCodeBlockTemplateSource } } },
630
+ };
631
+
632
+ /**
633
+ * Empty state — an initial view before the first turn. Use to show a welcome
634
+ * screen, quick prompts, or onboarding content above this surface.
635
+ */
636
+ export const Empty: Story = {
637
+ render: () => ({
638
+ components: { CkConversation },
639
+ template: '<CkConversation :messages="[]" style="height: 100%;" />',
640
+ }),
641
+ parameters: { docs: { source: { code: emptyTemplateSource } } },
642
+ };