@aleph-alpha/chat-kit 3.3.0 → 4.0.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 (98) hide show
  1. package/dist/{CkInput.vue_vue_type_script_setup_true_lang-BAcBFs6I.js → CkInput.vue_vue_type_script_setup_true_lang-DJN-nZ2-.js} +118 -71
  2. package/dist/adapters/responses-api.d.ts +11 -5
  3. package/dist/adapters/responses-api.d.ts.map +1 -1
  4. package/dist/adapters/responses-api.js +9 -3
  5. package/dist/chat-kit.css +8 -8
  6. package/dist/components/base/CkAgentMessage/types.d.ts +1 -1
  7. package/dist/components/base/{CkCopyMessageActionButton/CkCopyMessageActionButton.stories.d.ts → CkCopyMessageButton/CkCopyMessageButton.stories.d.ts} +3 -3
  8. package/dist/components/base/CkCopyMessageButton/CkCopyMessageButton.stories.d.ts.map +1 -0
  9. package/dist/components/base/{CkCopyMessageActionButton/CkCopyMessageActionButton.vue.d.ts → CkCopyMessageButton/CkCopyMessageButton.vue.d.ts} +4 -4
  10. package/dist/components/base/CkCopyMessageButton/CkCopyMessageButton.vue.d.ts.map +1 -0
  11. package/dist/components/base/CkCopyMessageButton/index.d.ts +3 -0
  12. package/dist/components/base/CkCopyMessageButton/index.d.ts.map +1 -0
  13. package/dist/components/base/{CkCopyMessageActionButton → CkCopyMessageButton}/types.d.ts +3 -3
  14. package/dist/components/base/CkCopyMessageButton/types.d.ts.map +1 -0
  15. package/dist/components/base/CkFeedbackButton/types.d.ts +1 -1
  16. package/dist/components/base/CkFeedbackDialog/types.d.ts +1 -1
  17. package/dist/components/base/{CkMessageActionButton/CkMessageActionButton.stories.d.ts → CkMessageButton/CkMessageButton.stories.d.ts} +3 -3
  18. package/dist/components/base/CkMessageButton/CkMessageButton.stories.d.ts.map +1 -0
  19. package/dist/components/base/{CkMessageActionButton/CkMessageActionButton.vue.d.ts → CkMessageButton/CkMessageButton.vue.d.ts} +3 -3
  20. package/dist/components/base/CkMessageButton/CkMessageButton.vue.d.ts.map +1 -0
  21. package/dist/components/base/CkMessageButton/index.d.ts +3 -0
  22. package/dist/components/base/CkMessageButton/index.d.ts.map +1 -0
  23. package/dist/components/base/{CkMessageActionButton → CkMessageButton}/types.d.ts +2 -2
  24. package/dist/components/base/CkMessageButton/types.d.ts.map +1 -0
  25. package/dist/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.stories.d.ts +16 -0
  26. package/dist/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.stories.d.ts.map +1 -0
  27. package/dist/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.vue.d.ts +12 -0
  28. package/dist/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.vue.d.ts.map +1 -0
  29. package/dist/components/base/CkRegenerateMessageButton/index.d.ts +3 -0
  30. package/dist/components/base/{CkCopyMessageActionButton → CkRegenerateMessageButton}/index.d.ts.map +1 -1
  31. package/dist/components/base/CkRegenerateMessageButton/types.d.ts +27 -0
  32. package/dist/components/base/CkRegenerateMessageButton/types.d.ts.map +1 -0
  33. package/dist/components/base/index.d.ts +3 -2
  34. package/dist/components/base/index.d.ts.map +1 -1
  35. package/dist/components/composed/CkConversation/CkConversation.stories.d.ts +1 -1
  36. package/dist/components/composed/CkConversation/types.d.ts +1 -1
  37. package/dist/components/index.d.ts +1 -1
  38. package/dist/components/index.d.ts.map +1 -1
  39. package/dist/components/index.js +7 -6
  40. package/dist/composables/index.js +1 -1
  41. package/dist/handlers/index.js +2 -2
  42. package/dist/handlers/useChatKitLabels.d.ts +2 -0
  43. package/dist/handlers/useChatKitLabels.d.ts.map +1 -1
  44. package/dist/handlers/useMessageHandler.d.ts +37 -0
  45. package/dist/handlers/useMessageHandler.d.ts.map +1 -1
  46. package/dist/helpers/conversationTree.d.ts +15 -0
  47. package/dist/helpers/conversationTree.d.ts.map +1 -1
  48. package/dist/{index-C6njJ8EB.js → index-CyrVnbS9.js} +1 -1
  49. package/dist/index.d.ts +1 -1
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +11 -10
  52. package/dist/{useChatKitLabels-BM2I7-9o.js → useChatKitLabels-zMFV7aEz.js} +1 -0
  53. package/dist/{useMessageHandler-CMdy-lm0.js → useMessageHandler-N50Yj-NU.js} +45 -7
  54. package/package.json +3 -3
  55. package/src/adapters/responses-api.spec.ts +52 -5
  56. package/src/adapters/responses-api.ts +26 -7
  57. package/src/components/base/CkAgentMessage/types.ts +1 -1
  58. package/src/components/base/{CkCopyMessageActionButton/CkCopyMessageActionButton.spec.ts → CkCopyMessageButton/CkCopyMessageButton.spec.ts} +6 -6
  59. package/src/components/base/{CkCopyMessageActionButton/CkCopyMessageActionButton.stories.ts → CkCopyMessageButton/CkCopyMessageButton.stories.ts} +8 -8
  60. package/src/components/base/{CkCopyMessageActionButton/CkCopyMessageActionButton.vue → CkCopyMessageButton/CkCopyMessageButton.vue} +6 -6
  61. package/src/components/base/CkCopyMessageButton/index.ts +2 -0
  62. package/src/components/base/{CkCopyMessageActionButton → CkCopyMessageButton}/types.ts +3 -3
  63. package/src/components/base/CkFeedbackButton/CkFeedbackButton.vue +5 -5
  64. package/src/components/base/CkFeedbackButton/types.ts +1 -1
  65. package/src/components/base/CkFeedbackDialog/types.ts +1 -1
  66. package/src/components/base/{CkMessageActionButton/CkMessageActionButton.spec.ts → CkMessageButton/CkMessageButton.spec.ts} +6 -6
  67. package/src/components/base/{CkMessageActionButton/CkMessageActionButton.stories.ts → CkMessageButton/CkMessageButton.stories.ts} +10 -10
  68. package/src/components/base/{CkMessageActionButton/CkMessageActionButton.vue → CkMessageButton/CkMessageButton.vue} +3 -3
  69. package/src/components/base/CkMessageButton/index.ts +2 -0
  70. package/src/components/base/{CkMessageActionButton → CkMessageButton}/types.ts +2 -2
  71. package/src/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.spec.ts +49 -0
  72. package/src/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.stories.ts +63 -0
  73. package/src/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.vue +43 -0
  74. package/src/components/base/CkRegenerateMessageButton/index.ts +2 -0
  75. package/src/components/base/CkRegenerateMessageButton/types.ts +27 -0
  76. package/src/components/base/index.ts +3 -2
  77. package/src/components/composed/CkConversation/CkConversation.stories.ts +6 -6
  78. package/src/components/composed/CkConversation/CkConversation.vue +2 -2
  79. package/src/components/composed/CkConversation/types.ts +1 -1
  80. package/src/components/index.ts +3 -2
  81. package/src/handlers/useChatKitLabels.spec.ts +1 -0
  82. package/src/handlers/useChatKitLabels.ts +3 -0
  83. package/src/handlers/useMessageHandler.spec.ts +79 -0
  84. package/src/handlers/useMessageHandler.ts +50 -2
  85. package/src/helpers/conversationTree.spec.ts +120 -0
  86. package/src/helpers/conversationTree.ts +57 -8
  87. package/src/index.ts +3 -2
  88. package/dist/components/base/CkCopyMessageActionButton/CkCopyMessageActionButton.stories.d.ts.map +0 -1
  89. package/dist/components/base/CkCopyMessageActionButton/CkCopyMessageActionButton.vue.d.ts.map +0 -1
  90. package/dist/components/base/CkCopyMessageActionButton/index.d.ts +0 -3
  91. package/dist/components/base/CkCopyMessageActionButton/types.d.ts.map +0 -1
  92. package/dist/components/base/CkMessageActionButton/CkMessageActionButton.stories.d.ts.map +0 -1
  93. package/dist/components/base/CkMessageActionButton/CkMessageActionButton.vue.d.ts.map +0 -1
  94. package/dist/components/base/CkMessageActionButton/index.d.ts +0 -3
  95. package/dist/components/base/CkMessageActionButton/index.d.ts.map +0 -1
  96. package/dist/components/base/CkMessageActionButton/types.d.ts.map +0 -1
  97. package/src/components/base/CkCopyMessageActionButton/index.ts +0 -2
  98. package/src/components/base/CkMessageActionButton/index.ts +0 -2
@@ -71,10 +71,8 @@ function updateNodeId(tree, oldId, newId) {
71
71
  }
72
72
  if (tree.rootId === oldId) tree.rootId = newId;
73
73
  }
74
- function cutBranchAfter(tree, id) {
75
- const node = tree.nodes[id];
76
- if (!node) return;
77
- const stack = [...node.childrenIds];
74
+ function deleteSubtrees(tree, ids) {
75
+ const stack = [...ids];
78
76
  while (stack.length > 0) {
79
77
  const currentId = stack.pop();
80
78
  const current = tree.nodes[currentId];
@@ -82,9 +80,33 @@ function cutBranchAfter(tree, id) {
82
80
  stack.push(...current.childrenIds);
83
81
  delete tree.nodes[currentId];
84
82
  }
83
+ }
84
+ function cutBranchAfter(tree, id) {
85
+ const node = tree.nodes[id];
86
+ if (!node) return;
87
+ deleteSubtrees(tree, node.childrenIds);
85
88
  node.childrenIds = [];
86
89
  node.activeChildIndex = 0;
87
90
  }
91
+ function cutBranchAt(tree, id) {
92
+ const node = tree.nodes[id];
93
+ if (!node) return;
94
+ deleteSubtrees(tree, [id]);
95
+ if (node.parentId === null) {
96
+ if (tree.rootId === id) tree.rootId = null;
97
+ return;
98
+ }
99
+ const parent = tree.nodes[node.parentId];
100
+ if (!parent) return;
101
+ const idx = parent.childrenIds.indexOf(id);
102
+ if (idx !== -1) {
103
+ parent.childrenIds.splice(idx, 1);
104
+ if (idx < parent.activeChildIndex) parent.activeChildIndex -= 1;
105
+ }
106
+ if (parent.activeChildIndex >= parent.childrenIds.length) {
107
+ parent.activeChildIndex = Math.max(0, parent.childrenIds.length - 1);
108
+ }
109
+ }
88
110
  function navigate(tree, targetId) {
89
111
  const node = tree.nodes[targetId];
90
112
  if (!node || node.parentId === null) return;
@@ -102,6 +124,13 @@ const useMessageHandler = () => {
102
124
  const list = messages.value;
103
125
  return list.length > 0 ? list[list.length - 1].id : null;
104
126
  });
127
+ function resolveParentId(parentId) {
128
+ if (parentId === void 0) return currentLeafId.value;
129
+ if (!tree.nodes[parentId]) {
130
+ throw new Error(`Unknown parent message id: ${parentId}`);
131
+ }
132
+ return parentId;
133
+ }
105
134
  function setCurrentUserMessage(text) {
106
135
  currentUserMessage.value = text;
107
136
  }
@@ -112,12 +141,13 @@ const useMessageHandler = () => {
112
141
  const id = generateId();
113
142
  const text = currentUserMessage.value.trim();
114
143
  if (!text) throw new Error("No message");
144
+ const parentId = resolveParentId(opts?.parentId);
115
145
  const content = [
116
146
  { type: "text", id: generateId(), text, status: "completed" }
117
147
  ];
118
148
  const node = {
119
149
  id,
120
- parentId: currentLeafId.value,
150
+ parentId,
121
151
  childrenIds: [],
122
152
  activeChildIndex: 0,
123
153
  role: "user",
@@ -150,7 +180,7 @@ const useMessageHandler = () => {
150
180
  } else {
151
181
  const node = {
152
182
  id: message.id,
153
- parentId: currentLeafId.value,
183
+ parentId: resolveParentId(message.parentId),
154
184
  childrenIds: [],
155
185
  activeChildIndex: 0,
156
186
  role: message.role,
@@ -191,6 +221,12 @@ const useMessageHandler = () => {
191
221
  function cutBranchAfter$1(messageId) {
192
222
  cutBranchAfter(tree, messageId);
193
223
  }
224
+ function cutBranchAt$1(messageId) {
225
+ cutBranchAt(tree, messageId);
226
+ }
227
+ function getTree() {
228
+ return readonly(tree);
229
+ }
194
230
  return {
195
231
  messages,
196
232
  currentUserMessage: readonly(currentUserMessage),
@@ -204,7 +240,9 @@ const useMessageHandler = () => {
204
240
  clearMessages,
205
241
  setTree,
206
242
  navigate: navigate$1,
207
- cutBranchAfter: cutBranchAfter$1
243
+ cutBranchAfter: cutBranchAfter$1,
244
+ cutBranchAt: cutBranchAt$1,
245
+ getTree
208
246
  };
209
247
  };
210
248
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aleph-alpha/chat-kit",
3
- "version": "3.3.0",
3
+ "version": "4.0.0",
4
4
  "description": "Chat kit package for building chat interfaces with Vue",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -102,9 +102,9 @@
102
102
  "vitest": "3.2.6",
103
103
  "vue": "^3.5.27",
104
104
  "vue-tsc": "^3.2.4",
105
- "@aleph-alpha/tsconfig-frontend": "0.7.0",
105
+ "@aleph-alpha/eslint-config-frontend": "0.7.0",
106
106
  "@aleph-alpha/prettier-config-frontend": "2.1.0",
107
- "@aleph-alpha/eslint-config-frontend": "0.7.0"
107
+ "@aleph-alpha/tsconfig-frontend": "0.7.0"
108
108
  },
109
109
  "scripts": {
110
110
  "build": "vite build && vue-tsc --project tsconfig.build.json --declaration --emitDeclarationOnly --outDir dist",
@@ -142,6 +142,34 @@ describe('responsesToTree', () => {
142
142
  expect(a1.activeChildIndex).toBe(1);
143
143
  });
144
144
 
145
+ it('favours the latest root branch when the first turn is branched into several root responses', () => {
146
+ const tree = responsesToTree([
147
+ makeResponse({
148
+ id: 'r1',
149
+ request_input: [userInput('First attempt')],
150
+ output: [assistantOutput('msg-a1', 'A1')],
151
+ }),
152
+ makeResponse({
153
+ id: 'r2',
154
+ request_input: [userInput('Second attempt')],
155
+ output: [assistantOutput('msg-a2', 'A2')],
156
+ }),
157
+ ]);
158
+
159
+ // Both turns are root-anchored (no previous_response_id); the latest wins
160
+ // as the active root so getLinearPath surfaces it.
161
+ expect(tree.nodes['user-r1-0']!.parentId).toBeNull();
162
+ expect(tree.nodes['user-r2-0']!.parentId).toBeNull();
163
+ expect(tree.rootId).toBe('user-r2-0');
164
+
165
+ const handler = useMessageHandler();
166
+ handler.setTree(tree);
167
+ expect(handler.messages.value.map((m) => m.id)).toEqual([
168
+ 'user-r2-0',
169
+ 'msg-a2',
170
+ ]);
171
+ });
172
+
145
173
  it('skips responses with no visible content but keeps descendants attached to the parent tail', () => {
146
174
  const tree = responsesToTree([
147
175
  makeResponse({
@@ -402,15 +430,31 @@ describe('responsesToTree', () => {
402
430
  // string on the wire (transports flatten nested metadata).
403
431
  metadata: {
404
432
  attachments: JSON.stringify([
405
- { fileName: 'report.pdf', fileId: 'f1', searchStoreId: 's1' },
433
+ {
434
+ fileName: 'report.pdf',
435
+ fileId: 'f1',
436
+ searchStoreId: 's1',
437
+ documentId: 'd1',
438
+ },
406
439
  ]),
407
440
  },
408
441
  }),
409
442
  ]);
410
443
 
444
+ // The full attachment shape (incl. `documentId`) is preserved so the
445
+ // turn can be faithfully re-submitted (e.g. regenerate) after a reload.
411
446
  expect(tree.nodes['user-r1-0']!.metadata).toEqual({
412
447
  responseId: 'r1',
413
- attachments: [{ name: 'report.pdf', fileId: 'f1', searchStoreId: 's1' }],
448
+ model: 'pharia-1',
449
+ attachments: [
450
+ {
451
+ name: 'report.pdf',
452
+ fileId: 'f1',
453
+ fileName: 'report.pdf',
454
+ searchStoreId: 's1',
455
+ documentId: 'd1',
456
+ },
457
+ ],
414
458
  });
415
459
  // The assistant turn does not inherit attachments but still carries
416
460
  // its responseId stamp.
@@ -431,9 +475,12 @@ describe('responsesToTree', () => {
431
475
  }),
432
476
  ]);
433
477
 
434
- // The malformed attachments are dropped, but `responseId` is still
435
- // stamped on every node the adapter emits.
436
- expect(tree.nodes['user-r1-0']!.metadata).toEqual({ responseId: 'r1' });
478
+ // The malformed attachments are dropped; the first user node still
479
+ // carries `responseId` (and `model` when present).
480
+ expect(tree.nodes['user-r1-0']!.metadata).toEqual({
481
+ responseId: 'r1',
482
+ model: 'pharia-1',
483
+ });
437
484
  expect(tree.nodes['user-r1-0']!.metadata.attachments).toBeUndefined();
438
485
  expect(warn).toHaveBeenCalled();
439
486
  } finally {
@@ -212,6 +212,7 @@ export interface ResponseAttachment {
212
212
  fileId: string;
213
213
  fileName: string;
214
214
  searchStoreId: string;
215
+ documentId?: string;
215
216
  }
216
217
 
217
218
  /**
@@ -219,15 +220,17 @@ export interface ResponseAttachment {
219
220
  * `metadata.attachments` on the user `TreeMessage` (chat-kit's `BaseMessage`
220
221
  * /`TreeMessage` carry `metadata` as an opaque `Record<string, unknown>`).
221
222
  *
222
- * The shape is intentionally minimal — a human-readable `name` plus the
223
- * optional ids needed to resolve a public URL for the file. Consumers
224
- * using a different attachment shape can either skip this adapter or
225
- * project this onto their own type at the read site.
223
+ * Carries a human-readable `name` plus every id needed to resolve a public
224
+ * URL for the file or to re-submit it on a later turn (e.g. regenerate).
225
+ * Consumers using a different attachment shape can either skip this adapter
226
+ * or project this onto their own type at the read site.
226
227
  */
227
228
  export interface ResponseMessageAttachment {
228
229
  name: string;
229
230
  fileId?: string;
231
+ fileName?: string;
230
232
  searchStoreId?: string;
233
+ documentId?: string;
231
234
  }
232
235
 
233
236
  /**
@@ -280,6 +283,7 @@ export interface ResponseList {
280
283
  */
281
284
  export type ResponseTreeMessageMetadata = MessageMetadata & {
282
285
  responseId: string;
286
+ model?: string;
283
287
  attachments?: ResponseMessageAttachment[];
284
288
  };
285
289
 
@@ -478,13 +482,18 @@ function responseToMessageNodes(resp: StoredResponse): ResponseMessageNode[] {
478
482
  const userContent = text || responseAttachments?.length;
479
483
  if (role !== 'user' || !userContent) continue;
480
484
 
481
- const metadata: ResponseTreeMessageMetadata = { responseId: resp.id };
485
+ const metadata: ResponseTreeMessageMetadata = {
486
+ responseId: resp.id,
487
+ ...(resp.model ? { model: resp.model } : {}),
488
+ };
482
489
  if (responseAttachments && userNodesPushed === 0) {
483
490
  metadata.attachments = responseAttachments.map(
484
491
  (a): ResponseMessageAttachment => ({
485
492
  name: a.fileName,
486
493
  fileId: a.fileId,
494
+ fileName: a.fileName,
487
495
  searchStoreId: a.searchStoreId,
496
+ ...(a.documentId ? { documentId: a.documentId } : {}),
488
497
  }),
489
498
  );
490
499
  }
@@ -607,6 +616,12 @@ function linkResponse(state: TreeBuildState, resp: StoredResponse): void {
607
616
  parentResponseId !== null
608
617
  ? (tailMessageByResponse.get(parentResponseId) ?? null)
609
618
  : null;
619
+ // Whether this response opens a new root-level branch (it chains onto
620
+ // nothing). The first turn can be branched/regenerated, producing several
621
+ // root responses; since the tree has a single `rootId` with no parent node
622
+ // to track sibling navigation, we keep the latest such branch as the active
623
+ // root — mirroring the "latest sibling wins" rule used for deeper branches.
624
+ const isRootChain = chainParent === null;
610
625
 
611
626
  const messageNodes = responseToMessageNodes(resp);
612
627
 
@@ -624,7 +639,9 @@ function linkResponse(state: TreeBuildState, resp: StoredResponse): void {
624
639
  chainParent = node.id;
625
640
  }
626
641
 
627
- if (state.rootMessageId === null && firstId !== null) {
642
+ // Favour the latest root branch: responses arrive in ascending `created_at`
643
+ // order, so a later root chain overwrites an earlier one.
644
+ if (firstId !== null && (state.rootMessageId === null || isRootChain)) {
628
645
  state.rootMessageId = firstId;
629
646
  }
630
647
 
@@ -639,7 +656,9 @@ function linkResponse(state: TreeBuildState, resp: StoredResponse): void {
639
656
  * pass: each response's `previous_response_id` always points to a response
640
657
  * we have already processed, so we can wire its messages onto the parent's
641
658
  * last message immediately. Sibling responses (same `previous_response_id`)
642
- * surface as branches; the latest one wins as the active branch.
659
+ * surface as branches; the latest one wins as the active branch. The same
660
+ * preference applies at the top level: when the first turn is branched into
661
+ * several root responses, the latest becomes the active `rootId`.
643
662
  *
644
663
  * Every emitted node carries `metadata.responseId` pointing back to the
645
664
  * originating `StoredResponse.id` (see {@link ResponseTreeMessage}). The
@@ -6,6 +6,6 @@
6
6
  * @category Messages
7
7
  * @useCases render assistant reply, AI message layout, conversation turn, streaming message container
8
8
  * @keywords agent, assistant, message, bubble, reply, ai, conversation
9
- * @related CkUserMessage, CkConversation, CkMarkdownRenderer, CkCopyMessageActionButton
9
+ * @related CkUserMessage, CkConversation, CkMarkdownRenderer, CkCopyMessageButton
10
10
  */
11
11
  export type CkAgentMessageProps = Record<string, never>;
@@ -1,10 +1,10 @@
1
- import CkCopyMessageActionButton from './CkCopyMessageActionButton.vue';
1
+ import CkCopyMessageButton from './CkCopyMessageButton.vue';
2
2
  import userEvent from '@testing-library/user-event';
3
3
  import { render, screen, waitFor } from '@testing-library/vue';
4
4
  import { flushPromises } from '@vue/test-utils';
5
5
  import { beforeEach, describe, expect, it, vi } from 'vitest';
6
6
 
7
- describe('CkCopyMessageActionButton', () => {
7
+ describe('CkCopyMessageButton', () => {
8
8
  const writeText = vi.fn((_: string) => Promise.resolve());
9
9
 
10
10
  beforeEach(() => {
@@ -14,7 +14,7 @@ describe('CkCopyMessageActionButton', () => {
14
14
 
15
15
  it('emits copy with success when clipboard succeeds', async () => {
16
16
  const user = userEvent.setup();
17
- const { emitted } = render(CkCopyMessageActionButton, {
17
+ const { emitted } = render(CkCopyMessageButton, {
18
18
  props: { text: 'x', writeToClipboard: writeText },
19
19
  });
20
20
 
@@ -27,7 +27,7 @@ describe('CkCopyMessageActionButton', () => {
27
27
 
28
28
  it('copies text and updates the accessible name', async () => {
29
29
  const user = userEvent.setup();
30
- render(CkCopyMessageActionButton, {
30
+ render(CkCopyMessageButton, {
31
31
  props: { text: 'Hello, world', writeToClipboard: writeText },
32
32
  });
33
33
 
@@ -50,7 +50,7 @@ describe('CkCopyMessageActionButton', () => {
50
50
 
51
51
  it('emits copy with success false', async () => {
52
52
  const user = userEvent.setup();
53
- const { emitted } = render(CkCopyMessageActionButton, {
53
+ const { emitted } = render(CkCopyMessageButton, {
54
54
  props: { text: 'x', writeToClipboard: writeText },
55
55
  });
56
56
 
@@ -67,7 +67,7 @@ describe('CkCopyMessageActionButton', () => {
67
67
  it('reverts label after durationMs', async () => {
68
68
  vi.useFakeTimers();
69
69
  const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
70
- render(CkCopyMessageActionButton, {
70
+ render(CkCopyMessageButton, {
71
71
  props: { text: 'x', durationMs: 1500, writeToClipboard: writeText },
72
72
  });
73
73
 
@@ -1,9 +1,9 @@
1
- import CkCopyMessageActionButton from './CkCopyMessageActionButton.vue';
1
+ import CkCopyMessageButton from './CkCopyMessageButton.vue';
2
2
  import type { Meta, StoryObj } from '@storybook/vue3-vite';
3
3
 
4
- const meta: Meta<typeof CkCopyMessageActionButton> = {
5
- title: 'Base/CkCopyMessageActionButton',
6
- component: CkCopyMessageActionButton,
4
+ const meta: Meta<typeof CkCopyMessageButton> = {
5
+ title: 'Base/CkCopyMessageButton',
6
+ component: CkCopyMessageButton,
7
7
  tags: ['autodocs'],
8
8
  argTypes: {
9
9
  text: {
@@ -31,19 +31,19 @@ export default meta;
31
31
  type Story = StoryObj<typeof meta>;
32
32
 
33
33
  const defaultTemplateSource = `<script setup lang="ts">
34
- import { CkCopyMessageActionButton } from '@aleph-alpha/chat-kit';
34
+ import { CkCopyMessageButton } from '@aleph-alpha/chat-kit';
35
35
  </script>
36
36
 
37
37
  <template>
38
- <CkCopyMessageActionButton text="Sample assistant reply text to copy." />
38
+ <CkCopyMessageButton text="Sample assistant reply text to copy." />
39
39
  </template>`;
40
40
 
41
41
  const disabledTemplateSource = `<script setup lang="ts">
42
- import { CkCopyMessageActionButton } from '@aleph-alpha/chat-kit';
42
+ import { CkCopyMessageButton } from '@aleph-alpha/chat-kit';
43
43
  </script>
44
44
 
45
45
  <template>
46
- <CkCopyMessageActionButton text="Cannot copy when disabled." disabled />
46
+ <CkCopyMessageButton text="Cannot copy when disabled." disabled />
47
47
  </template>`;
48
48
 
49
49
  /**
@@ -1,14 +1,14 @@
1
1
  <script setup lang="ts">
2
- import { CkMessageActionButton } from '../CkMessageActionButton';
3
- import type { CkCopyMessageActionButtonProps } from './types';
2
+ import { CkMessageButton } from '../CkMessageButton';
3
+ import type { CkCopyMessageButtonProps } from './types';
4
4
  import { UiIcon, UiTooltip } from '@aleph-alpha/ui-library';
5
5
  import { onUnmounted, ref } from 'vue';
6
6
 
7
7
  defineOptions({
8
- name: 'CkCopyMessageActionButton',
8
+ name: 'CkCopyMessageButton',
9
9
  });
10
10
 
11
- const props = withDefaults(defineProps<CkCopyMessageActionButtonProps>(), {
11
+ const props = withDefaults(defineProps<CkCopyMessageButtonProps>(), {
12
12
  disabled: false,
13
13
  durationMs: 3000,
14
14
  ariaLabelCopy: 'Copy message',
@@ -68,7 +68,7 @@ async function handleClick() {
68
68
  align="center"
69
69
  :delay-duration="0"
70
70
  >
71
- <CkMessageActionButton
71
+ <CkMessageButton
72
72
  v-bind="{ ariaLabel: copied ? ariaLabelCopied : ariaLabelCopy }"
73
73
  :disabled="disabled"
74
74
  @click="handleClick"
@@ -83,7 +83,7 @@ async function handleClick() {
83
83
  />
84
84
  <UiIcon v-else key="check" name="check" :size="16" aria-hidden="true" />
85
85
  </Transition>
86
- </CkMessageActionButton>
86
+ </CkMessageButton>
87
87
  <span class="sr-only" aria-live="polite">
88
88
  {{ copied ? ariaLabelCopied : ariaLabelCopy }}
89
89
  </span>
@@ -0,0 +1,2 @@
1
+ export { default as CkCopyMessageButton } from './CkCopyMessageButton.vue';
2
+ export type * from './types';
@@ -5,9 +5,9 @@
5
5
  * @category Actions
6
6
  * @useCases copy assistant reply, copy message to clipboard, chat message action, feedback action on message footer
7
7
  * @keywords copy, clipboard, action, message, button, icon, feedback
8
- * @related CkMessageActionButton, CkAgentMessage, CkConversation
8
+ * @related CkMessageButton, CkAgentMessage, CkConversation
9
9
  */
10
- export interface CkCopyMessageActionButtonProps {
10
+ export interface CkCopyMessageButtonProps {
11
11
  /** Plain text to copy to the clipboard. */
12
12
  text: string;
13
13
  /**
@@ -37,7 +37,7 @@ export interface CkCopyMessageActionButtonProps {
37
37
  writeToClipboard?: (text: string) => Promise<void>;
38
38
  }
39
39
 
40
- export interface CkCopyMessageActionButtonEmits {
40
+ export interface CkCopyMessageButtonEmits {
41
41
  /** Emitted after the clipboard operation finishes (success or failure). */
42
42
  (e: 'copy', payload: { success: boolean }): void;
43
43
  }
@@ -3,7 +3,7 @@ import {
3
3
  defaultChatKitLabels,
4
4
  useChatKitLabels,
5
5
  } from '../../../handlers/useChatKitLabels';
6
- import { CkMessageActionButton } from '../CkMessageActionButton';
6
+ import { CkMessageButton } from '../CkMessageButton';
7
7
  import type { CkFeedbackButtonProps, CkFeedbackValue } from './types';
8
8
  import { UiIcon, UiTooltip } from '@aleph-alpha/ui-library';
9
9
  import { computed } from 'vue';
@@ -81,7 +81,7 @@ function handleClick(target: 'up' | 'down'): void {
81
81
  align="center"
82
82
  :delay-duration="0"
83
83
  >
84
- <CkMessageActionButton
84
+ <CkMessageButton
85
85
  v-bind="{ ariaLabel: thumbsUpLabel }"
86
86
  :aria-pressed="isUpSelected"
87
87
  :aria-disabled="locked || undefined"
@@ -98,7 +98,7 @@ function handleClick(target: 'up' | 'down'): void {
98
98
  aria-hidden="true"
99
99
  :class="isUpSelected ? 'fill-current' : undefined"
100
100
  />
101
- </CkMessageActionButton>
101
+ </CkMessageButton>
102
102
  </UiTooltip>
103
103
  <UiTooltip
104
104
  :content="thumbsDownLabel"
@@ -106,7 +106,7 @@ function handleClick(target: 'up' | 'down'): void {
106
106
  align="center"
107
107
  :delay-duration="0"
108
108
  >
109
- <CkMessageActionButton
109
+ <CkMessageButton
110
110
  v-bind="{ ariaLabel: thumbsDownLabel }"
111
111
  :aria-pressed="isDownSelected"
112
112
  :aria-disabled="locked || undefined"
@@ -123,7 +123,7 @@ function handleClick(target: 'up' | 'down'): void {
123
123
  aria-hidden="true"
124
124
  :class="isDownSelected ? 'fill-current' : undefined"
125
125
  />
126
- </CkMessageActionButton>
126
+ </CkMessageButton>
127
127
  </UiTooltip>
128
128
  </div>
129
129
  </template>
@@ -10,7 +10,7 @@
10
10
  * @category Feedback
11
11
  * @useCases like or dislike an assistant reply, collect binary message rating, lock feedback after submission, rate AI response
12
12
  * @keywords feedback, thumbs up, thumbs down, like, dislike, rating, vote, action, message, button
13
- * @related CkMessageActionButton, CkCopyMessageActionButton, CkAgentMessage, CkConversation
13
+ * @related CkMessageButton, CkCopyMessageButton, CkAgentMessage, CkConversation
14
14
  */
15
15
  export type CkFeedbackValue = 'up' | 'down' | null;
16
16
 
@@ -18,7 +18,7 @@
18
18
  * capture a free-text comment, drive a two-step rating flow
19
19
  * @keywords feedback, dialog, modal, reason codes, free text, qualitative,
20
20
  * rating, comment
21
- * @related CkFeedbackButton, CkMessageActionButton, CkAgentMessage,
21
+ * @related CkFeedbackButton, CkMessageButton, CkAgentMessage,
22
22
  * CkConversation, UiDialog, UiToggleGroup
23
23
  */
24
24
  export type CkFeedbackSentiment = 'positive' | 'negative';
@@ -1,10 +1,10 @@
1
- import CkMessageActionButton from './CkMessageActionButton.vue';
1
+ import CkMessageButton from './CkMessageButton.vue';
2
2
  import { render, screen } from '@testing-library/vue';
3
3
  import { describe, expect, it } from 'vitest';
4
4
 
5
- describe('CkMessageActionButton', () => {
5
+ describe('CkMessageButton', () => {
6
6
  it('renders slot content inside the button', () => {
7
- render(CkMessageActionButton, {
7
+ render(CkMessageButton, {
8
8
  props: { ariaLabel: 'Action' },
9
9
  slots: { default: '<svg data-testid="icon"></svg>' },
10
10
  });
@@ -13,7 +13,7 @@ describe('CkMessageActionButton', () => {
13
13
  });
14
14
 
15
15
  it('sets the accessible label on the button', () => {
16
- render(CkMessageActionButton, {
16
+ render(CkMessageButton, {
17
17
  props: { ariaLabel: 'Copy message' },
18
18
  });
19
19
 
@@ -23,7 +23,7 @@ describe('CkMessageActionButton', () => {
23
23
  });
24
24
 
25
25
  it('passes disabled state to the button', () => {
26
- render(CkMessageActionButton, {
26
+ render(CkMessageButton, {
27
27
  props: { ariaLabel: 'Action', disabled: true },
28
28
  });
29
29
 
@@ -31,7 +31,7 @@ describe('CkMessageActionButton', () => {
31
31
  });
32
32
 
33
33
  it('defaults to enabled', () => {
34
- render(CkMessageActionButton, {
34
+ render(CkMessageButton, {
35
35
  props: { ariaLabel: 'Action' },
36
36
  });
37
37
 
@@ -1,10 +1,10 @@
1
- import CkMessageActionButton from './CkMessageActionButton.vue';
1
+ import CkMessageButton from './CkMessageButton.vue';
2
2
  import type { Meta, StoryObj } from '@storybook/vue3-vite';
3
3
  import { UiIcon } from '@aleph-alpha/ui-library';
4
4
 
5
- const meta: Meta<typeof CkMessageActionButton> = {
6
- title: 'Base/CkMessageActionButton',
7
- component: CkMessageActionButton,
5
+ const meta: Meta<typeof CkMessageButton> = {
6
+ title: 'Base/CkMessageButton',
7
+ component: CkMessageButton,
8
8
  tags: ['autodocs'],
9
9
  argTypes: {
10
10
  ariaLabel: {
@@ -29,14 +29,14 @@ export default meta;
29
29
  type Story = StoryObj<typeof meta>;
30
30
 
31
31
  const defaultTemplateSource = `<script setup lang="ts">
32
- import { CkMessageActionButton } from '@aleph-alpha/chat-kit';
32
+ import { CkMessageButton } from '@aleph-alpha/chat-kit';
33
33
  import { UiIcon } from '@aleph-alpha/ui-library';
34
34
  </script>
35
35
 
36
36
  <template>
37
- <CkMessageActionButton ariaLabel="Copy message">
37
+ <CkMessageButton ariaLabel="Copy message">
38
38
  <UiIcon name="copy" :size="16" aria-hidden="true" />
39
- </CkMessageActionButton>
39
+ </CkMessageButton>
40
40
  </template>`;
41
41
 
42
42
  /**
@@ -45,11 +45,11 @@ import { UiIcon } from '@aleph-alpha/ui-library';
45
45
  */
46
46
  export const Default: Story = {
47
47
  render: () => ({
48
- components: { CkMessageActionButton, UiIcon },
48
+ components: { CkMessageButton, UiIcon },
49
49
  template: `
50
- <CkMessageActionButton ariaLabel="Copy message">
50
+ <CkMessageButton ariaLabel="Copy message">
51
51
  <UiIcon name="copy" :size="16" aria-hidden="true" />
52
- </CkMessageActionButton>
52
+ </CkMessageButton>
53
53
  `,
54
54
  }),
55
55
  parameters: { docs: { source: { code: defaultTemplateSource } } },
@@ -1,12 +1,12 @@
1
1
  <script setup lang="ts">
2
- import type { CkMessageActionButtonProps } from './types';
2
+ import type { CkMessageButtonProps } from './types';
3
3
  import { UiButton } from '@aleph-alpha/ui-library';
4
4
 
5
5
  defineOptions({
6
- name: 'CkMessageActionButton',
6
+ name: 'CkMessageButton',
7
7
  });
8
8
 
9
- withDefaults(defineProps<CkMessageActionButtonProps>(), {
9
+ withDefaults(defineProps<CkMessageButtonProps>(), {
10
10
  disabled: false,
11
11
  });
12
12
  </script>
@@ -0,0 +1,2 @@
1
+ export { default as CkMessageButton } from './CkMessageButton.vue';
2
+ export type * from './types';
@@ -5,9 +5,9 @@
5
5
  * @category Actions
6
6
  * @useCases message footer action, like/dislike reaction, share button on reply, secondary message action
7
7
  * @keywords icon button, action, message, ghost, accessible, aria-label
8
- * @related CkCopyMessageActionButton, CkAgentMessage, CkConversation
8
+ * @related CkCopyMessageButton, CkAgentMessage, CkConversation
9
9
  */
10
- export interface CkMessageActionButtonProps {
10
+ export interface CkMessageButtonProps {
11
11
  /**
12
12
  * Accessible label for the button. **Required** because the button only
13
13
  * renders an icon.