@aleph-alpha/chat-kit 3.2.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 (164) hide show
  1. package/dist/{CkInput.vue_vue_type_script_setup_true_lang-B-4xnH2c.js → CkInput.vue_vue_type_script_setup_true_lang-DJN-nZ2-.js} +391 -113
  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 +14 -8
  6. package/dist/components/base/CkAgentMessage/CkAgentMessage.stories.d.ts +7 -0
  7. package/dist/components/base/CkAgentMessage/CkAgentMessage.stories.d.ts.map +1 -1
  8. package/dist/components/base/CkAgentMessage/types.d.ts +1 -1
  9. package/dist/components/base/CkCitation/CkCitation.stories.d.ts +47 -0
  10. package/dist/components/base/CkCitation/CkCitation.stories.d.ts.map +1 -0
  11. package/dist/components/base/CkCitation/CkCitation.vue.d.ts +15 -0
  12. package/dist/components/base/CkCitation/CkCitation.vue.d.ts.map +1 -0
  13. package/dist/components/base/CkCitation/index.d.ts +3 -0
  14. package/dist/components/base/CkCitation/index.d.ts.map +1 -0
  15. package/dist/components/base/CkCitation/types.d.ts +79 -0
  16. package/dist/components/base/CkCitation/types.d.ts.map +1 -0
  17. package/dist/components/base/CkCitationCard/CkCitationCard.stories.d.ts +51 -0
  18. package/dist/components/base/CkCitationCard/CkCitationCard.stories.d.ts.map +1 -0
  19. package/dist/components/base/CkCitationCard/CkCitationCard.vue.d.ts +13 -0
  20. package/dist/components/base/CkCitationCard/CkCitationCard.vue.d.ts.map +1 -0
  21. package/dist/components/base/CkCitationCard/index.d.ts +3 -0
  22. package/dist/components/base/CkCitationCard/index.d.ts.map +1 -0
  23. package/dist/components/base/CkCitationCard/types.d.ts +51 -0
  24. package/dist/components/base/CkCitationCard/types.d.ts.map +1 -0
  25. package/dist/components/base/{CkCopyMessageActionButton/CkCopyMessageActionButton.stories.d.ts → CkCopyMessageButton/CkCopyMessageButton.stories.d.ts} +3 -3
  26. package/dist/components/base/CkCopyMessageButton/CkCopyMessageButton.stories.d.ts.map +1 -0
  27. package/dist/components/base/{CkCopyMessageActionButton/CkCopyMessageActionButton.vue.d.ts → CkCopyMessageButton/CkCopyMessageButton.vue.d.ts} +4 -4
  28. package/dist/components/base/CkCopyMessageButton/CkCopyMessageButton.vue.d.ts.map +1 -0
  29. package/dist/components/base/CkCopyMessageButton/index.d.ts +3 -0
  30. package/dist/components/base/CkCopyMessageButton/index.d.ts.map +1 -0
  31. package/dist/components/base/{CkCopyMessageActionButton → CkCopyMessageButton}/types.d.ts +3 -3
  32. package/dist/components/base/CkCopyMessageButton/types.d.ts.map +1 -0
  33. package/dist/components/base/CkDropZoneOverlay/CkDropZoneOverlay.vue.d.ts +1 -1
  34. package/dist/components/base/CkFeedbackButton/types.d.ts +1 -1
  35. package/dist/components/base/CkFeedbackDialog/CkFeedbackDialog.vue.d.ts +2 -2
  36. package/dist/components/base/CkFeedbackDialog/types.d.ts +1 -1
  37. package/dist/components/base/CkFileItemsList/CkFileItemsList.vue.d.ts +1 -1
  38. package/dist/components/base/CkMarkdownRenderer/CkMarkdownRenderer.stories.d.ts +15 -0
  39. package/dist/components/base/CkMarkdownRenderer/CkMarkdownRenderer.stories.d.ts.map +1 -1
  40. package/dist/components/base/CkMarkdownRenderer/CkMarkdownRenderer.vue.d.ts +16 -2
  41. package/dist/components/base/CkMarkdownRenderer/CkMarkdownRenderer.vue.d.ts.map +1 -1
  42. package/dist/components/base/CkMarkdownRenderer/index.d.ts +1 -1
  43. package/dist/components/base/CkMarkdownRenderer/index.d.ts.map +1 -1
  44. package/dist/components/base/CkMarkdownRenderer/types.d.ts +25 -0
  45. package/dist/components/base/CkMarkdownRenderer/types.d.ts.map +1 -1
  46. package/dist/components/base/CkMarkdownRenderer/useCitationRendering.d.ts +17 -0
  47. package/dist/components/base/CkMarkdownRenderer/useCitationRendering.d.ts.map +1 -0
  48. package/dist/components/base/{CkMessageActionButton/CkMessageActionButton.stories.d.ts → CkMessageButton/CkMessageButton.stories.d.ts} +3 -3
  49. package/dist/components/base/CkMessageButton/CkMessageButton.stories.d.ts.map +1 -0
  50. package/dist/components/base/{CkMessageActionButton/CkMessageActionButton.vue.d.ts → CkMessageButton/CkMessageButton.vue.d.ts} +3 -3
  51. package/dist/components/base/CkMessageButton/CkMessageButton.vue.d.ts.map +1 -0
  52. package/dist/components/base/CkMessageButton/index.d.ts +3 -0
  53. package/dist/components/base/CkMessageButton/index.d.ts.map +1 -0
  54. package/dist/components/base/{CkMessageActionButton → CkMessageButton}/types.d.ts +2 -2
  55. package/dist/components/base/CkMessageButton/types.d.ts.map +1 -0
  56. package/dist/components/base/CkPromptSendButton/CkPromptSendButton.vue.d.ts +1 -1
  57. package/dist/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.stories.d.ts +16 -0
  58. package/dist/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.stories.d.ts.map +1 -0
  59. package/dist/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.vue.d.ts +12 -0
  60. package/dist/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.vue.d.ts.map +1 -0
  61. package/dist/components/base/CkRegenerateMessageButton/index.d.ts +3 -0
  62. package/dist/components/base/{CkCopyMessageActionButton → CkRegenerateMessageButton}/index.d.ts.map +1 -1
  63. package/dist/components/base/CkRegenerateMessageButton/types.d.ts +27 -0
  64. package/dist/components/base/CkRegenerateMessageButton/types.d.ts.map +1 -0
  65. package/dist/components/base/index.d.ts +8 -3
  66. package/dist/components/base/index.d.ts.map +1 -1
  67. package/dist/components/composed/CkConversation/CkConversation.stories.d.ts +1 -1
  68. package/dist/components/composed/CkConversation/types.d.ts +1 -1
  69. package/dist/components/composed/CkInput/CkInput.vue.d.ts +1 -1
  70. package/dist/components/index.d.ts +2 -2
  71. package/dist/components/index.d.ts.map +1 -1
  72. package/dist/components/index.js +26 -23
  73. package/dist/composables/fileUpload/useFileUpload.d.ts.map +1 -1
  74. package/dist/composables/fileUpload/useFileUploadWrapper.d.ts.map +1 -1
  75. package/dist/composables/index.js +1 -1
  76. package/dist/handlers/index.d.ts +2 -0
  77. package/dist/handlers/index.d.ts.map +1 -1
  78. package/dist/handlers/index.js +4 -3
  79. package/dist/handlers/useChatKitLabels.d.ts +13 -0
  80. package/dist/handlers/useChatKitLabels.d.ts.map +1 -1
  81. package/dist/handlers/useMarkdownParser.d.ts.map +1 -1
  82. package/dist/handlers/useMessageHandler.d.ts +37 -0
  83. package/dist/handlers/useMessageHandler.d.ts.map +1 -1
  84. package/dist/helpers/conversationTree.d.ts +15 -0
  85. package/dist/helpers/conversationTree.d.ts.map +1 -1
  86. package/dist/helpers/unified/remarkCitations.d.ts +17 -0
  87. package/dist/helpers/unified/remarkCitations.d.ts.map +1 -0
  88. package/dist/{index-DAmWGYNH.js → index-CyrVnbS9.js} +86 -71
  89. package/dist/index.d.ts +2 -2
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +30 -27
  92. package/dist/{useChatKitLabels-D5gNv8se.js → useChatKitLabels-zMFV7aEz.js} +31 -3
  93. package/dist/{useMessageHandler-CMdy-lm0.js → useMessageHandler-N50Yj-NU.js} +45 -7
  94. package/package.json +5 -3
  95. package/src/adapters/responses-api.spec.ts +52 -5
  96. package/src/adapters/responses-api.ts +26 -7
  97. package/src/components/base/CkAgentMessage/CkAgentMessage.stories.ts +112 -0
  98. package/src/components/base/CkAgentMessage/types.ts +1 -1
  99. package/src/components/base/CkCitation/CkCitation.spec.ts +79 -0
  100. package/src/components/base/CkCitation/CkCitation.stories.ts +136 -0
  101. package/src/components/base/CkCitation/CkCitation.vue +145 -0
  102. package/src/components/base/CkCitation/index.ts +2 -0
  103. package/src/components/base/CkCitation/types.ts +80 -0
  104. package/src/components/base/CkCitationCard/CkCitationCard.spec.ts +77 -0
  105. package/src/components/base/CkCitationCard/CkCitationCard.stories.ts +144 -0
  106. package/src/components/base/CkCitationCard/CkCitationCard.vue +76 -0
  107. package/src/components/base/CkCitationCard/index.ts +2 -0
  108. package/src/components/base/CkCitationCard/types.ts +51 -0
  109. package/src/components/base/{CkCopyMessageActionButton/CkCopyMessageActionButton.spec.ts → CkCopyMessageButton/CkCopyMessageButton.spec.ts} +6 -6
  110. package/src/components/base/{CkCopyMessageActionButton/CkCopyMessageActionButton.stories.ts → CkCopyMessageButton/CkCopyMessageButton.stories.ts} +8 -8
  111. package/src/components/base/{CkCopyMessageActionButton/CkCopyMessageActionButton.vue → CkCopyMessageButton/CkCopyMessageButton.vue} +6 -6
  112. package/src/components/base/CkCopyMessageButton/index.ts +2 -0
  113. package/src/components/base/{CkCopyMessageActionButton → CkCopyMessageButton}/types.ts +3 -3
  114. package/src/components/base/CkFeedbackButton/CkFeedbackButton.vue +5 -5
  115. package/src/components/base/CkFeedbackButton/types.ts +1 -1
  116. package/src/components/base/CkFeedbackDialog/types.ts +1 -1
  117. package/src/components/base/CkMarkdownRenderer/CkMarkdownRenderer.spec.ts +124 -0
  118. package/src/components/base/CkMarkdownRenderer/CkMarkdownRenderer.stories.ts +118 -0
  119. package/src/components/base/CkMarkdownRenderer/CkMarkdownRenderer.vue +70 -28
  120. package/src/components/base/CkMarkdownRenderer/index.ts +1 -0
  121. package/src/components/base/CkMarkdownRenderer/types.ts +26 -0
  122. package/src/components/base/CkMarkdownRenderer/useCitationRendering.spec.ts +94 -0
  123. package/src/components/base/CkMarkdownRenderer/useCitationRendering.ts +52 -0
  124. package/src/components/base/{CkMessageActionButton/CkMessageActionButton.spec.ts → CkMessageButton/CkMessageButton.spec.ts} +6 -6
  125. package/src/components/base/{CkMessageActionButton/CkMessageActionButton.stories.ts → CkMessageButton/CkMessageButton.stories.ts} +10 -10
  126. package/src/components/base/{CkMessageActionButton/CkMessageActionButton.vue → CkMessageButton/CkMessageButton.vue} +3 -3
  127. package/src/components/base/CkMessageButton/index.ts +2 -0
  128. package/src/components/base/{CkMessageActionButton → CkMessageButton}/types.ts +2 -2
  129. package/src/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.spec.ts +49 -0
  130. package/src/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.stories.ts +63 -0
  131. package/src/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.vue +43 -0
  132. package/src/components/base/CkRegenerateMessageButton/index.ts +2 -0
  133. package/src/components/base/CkRegenerateMessageButton/types.ts +27 -0
  134. package/src/components/base/index.ts +15 -2
  135. package/src/components/composed/CkConversation/CkConversation.stories.ts +6 -6
  136. package/src/components/composed/CkConversation/CkConversation.vue +2 -2
  137. package/src/components/composed/CkConversation/types.ts +1 -1
  138. package/src/components/index.ts +11 -2
  139. package/src/composables/fileUpload/useFileUpload.spec.ts +2 -61
  140. package/src/composables/fileUpload/useFileUpload.ts +2 -9
  141. package/src/composables/fileUpload/useFileUploadWrapper.spec.ts +195 -14
  142. package/src/composables/fileUpload/useFileUploadWrapper.ts +114 -81
  143. package/src/handlers/index.ts +2 -0
  144. package/src/handlers/useChatKitLabels.spec.ts +1 -0
  145. package/src/handlers/useChatKitLabels.ts +16 -0
  146. package/src/handlers/useMarkdownParser.ts +5 -2
  147. package/src/handlers/useMessageHandler.spec.ts +79 -0
  148. package/src/handlers/useMessageHandler.ts +50 -2
  149. package/src/helpers/conversationTree.spec.ts +120 -0
  150. package/src/helpers/conversationTree.ts +57 -8
  151. package/src/helpers/unified/remarkCitations.spec.ts +112 -0
  152. package/src/helpers/unified/remarkCitations.ts +55 -0
  153. package/src/index.ts +11 -2
  154. package/dist/components/base/CkCopyMessageActionButton/CkCopyMessageActionButton.stories.d.ts.map +0 -1
  155. package/dist/components/base/CkCopyMessageActionButton/CkCopyMessageActionButton.vue.d.ts.map +0 -1
  156. package/dist/components/base/CkCopyMessageActionButton/index.d.ts +0 -3
  157. package/dist/components/base/CkCopyMessageActionButton/types.d.ts.map +0 -1
  158. package/dist/components/base/CkMessageActionButton/CkMessageActionButton.stories.d.ts.map +0 -1
  159. package/dist/components/base/CkMessageActionButton/CkMessageActionButton.vue.d.ts.map +0 -1
  160. package/dist/components/base/CkMessageActionButton/index.d.ts +0 -3
  161. package/dist/components/base/CkMessageActionButton/index.d.ts.map +0 -1
  162. package/dist/components/base/CkMessageActionButton/types.d.ts.map +0 -1
  163. package/src/components/base/CkCopyMessageActionButton/index.ts +0 -2
  164. package/src/components/base/CkMessageActionButton/index.ts +0 -2
@@ -0,0 +1,77 @@
1
+ import CkCitationCard from './CkCitationCard.vue';
2
+ import { provideChatKitLabels } from '../../../handlers/useChatKitLabels';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { render, screen } from '@testing-library/vue';
5
+ import { defineComponent, h } from 'vue';
6
+
7
+ const base = {
8
+ fileName: 'q3-report.pdf',
9
+ snippet: 'The cited chunk text.',
10
+ };
11
+
12
+ describe('CkCitationCard', () => {
13
+ it('renders the file name and snippet', () => {
14
+ render(CkCitationCard, { props: { ...base, locationLabel: 'Page 3' } });
15
+
16
+ expect(screen.getByText('q3-report.pdf')).toBeInTheDocument();
17
+ expect(screen.getByText('The cited chunk text.')).toBeInTheDocument();
18
+ expect(screen.getByText('Page 3')).toBeInTheDocument();
19
+ });
20
+
21
+ it('hides the location footer when no locationLabel is given', () => {
22
+ render(CkCitationCard, { props: base });
23
+
24
+ expect(screen.queryByText('Page 3')).not.toBeInTheDocument();
25
+ });
26
+
27
+ it('hides the external-link action when no url is provided', () => {
28
+ render(CkCitationCard, { props: base });
29
+
30
+ expect(
31
+ screen.queryByRole('button', { name: 'Open source' }),
32
+ ).not.toBeInTheDocument();
33
+ });
34
+
35
+ it('shows the external-link action and emits open-source with the url', async () => {
36
+ const user = userEvent.setup();
37
+ const { emitted } = render(CkCitationCard, {
38
+ props: { ...base, url: '/api/v1/.../public-urls' },
39
+ });
40
+
41
+ await user.click(screen.getByRole('button', { name: 'Open source' }));
42
+
43
+ expect(emitted('open-source')).toEqual([['/api/v1/.../public-urls']]);
44
+ });
45
+
46
+ it('uses a custom openSourceLabel for the action', () => {
47
+ render(CkCitationCard, {
48
+ props: { ...base, url: '/u', openSourceLabel: 'Quelle öffnen' },
49
+ });
50
+
51
+ expect(
52
+ screen.getByRole('button', { name: 'Quelle öffnen' }),
53
+ ).toBeInTheDocument();
54
+ });
55
+
56
+ it('wraps a long unbroken snippet so it cannot overflow the fixed-width card', () => {
57
+ const snippet = 'x'.repeat(200);
58
+ render(CkCitationCard, { props: { ...base, snippet } });
59
+
60
+ expect(screen.getByText(snippet).className).toContain('break-words');
61
+ });
62
+
63
+ it('uses the injected citationOpenSource label via provideChatKitLabels', () => {
64
+ const Wrapper = defineComponent({
65
+ setup() {
66
+ provideChatKitLabels(() => ({ citationOpenSource: 'Quelle öffnen' }));
67
+ return () => h(CkCitationCard, { ...base, url: '/u' });
68
+ },
69
+ });
70
+
71
+ render(Wrapper);
72
+
73
+ expect(
74
+ screen.getByRole('button', { name: 'Quelle öffnen' }),
75
+ ).toBeInTheDocument();
76
+ });
77
+ });
@@ -0,0 +1,144 @@
1
+ import CkCitationCard from './CkCitationCard.vue';
2
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
3
+
4
+ const LOREM =
5
+ 'Lorem ipsum dolor sit amet consectetur. Phasellus ipsum nunc pellentesque diam donec id fermentum. Morbi amet lacus cras sagittis sagittis libero. Elit netus sodales in risus diam ut.';
6
+
7
+ /**
8
+ * CkCitationCard is the floating card that describes a single citation source:
9
+ * the file, the cited excerpt, an optional location (e.g. page), and an
10
+ * optional external-link action. It is the hover/focus content of `CkCitation`,
11
+ * but can be used on its own wherever a source preview is needed. The
12
+ * external-link action is exposed as an `open-source` event rather than a plain
13
+ * link, because the source URL is often auth-proxied.
14
+ */
15
+ const meta: Meta<typeof CkCitationCard> = {
16
+ title: 'Base/CkCitationCard',
17
+ component: CkCitationCard,
18
+ tags: ['autodocs'],
19
+ argTypes: {
20
+ fileName: { control: 'text' },
21
+ snippet: { control: 'text' },
22
+ locationLabel: { control: 'text' },
23
+ url: { control: 'text' },
24
+ openSourceLabel: { control: 'text' },
25
+ },
26
+ args: {
27
+ fileName: 'Q2_Business_Plan.pdf',
28
+ snippet: LOREM,
29
+ locationLabel: 'Page 3',
30
+ url: '/api/v1/search-stores/ss_abc/files/file_123/public-urls',
31
+ },
32
+ decorators: [
33
+ () => ({
34
+ template:
35
+ '<div style="max-width: 640px; margin: 0 auto; padding: 24px;"><story /></div>',
36
+ }),
37
+ ],
38
+ };
39
+
40
+ export default meta;
41
+ type Story = StoryObj<typeof meta>;
42
+
43
+ const defaultTemplateSource = `<script setup lang="ts">
44
+ import { CkCitationCard } from '@aleph-alpha/chat-kit';
45
+
46
+ function openSource(url: string) {
47
+ // The source URL is often auth-proxied — the host opens it.
48
+ window.open(url, '_blank', 'noopener');
49
+ }
50
+ </script>
51
+
52
+ <template>
53
+ <CkCitationCard
54
+ file-name="Q2_Business_Plan.pdf"
55
+ snippet="Lorem ipsum dolor sit amet consectetur…"
56
+ location-label="Page 3"
57
+ url="/api/v1/search-stores/ss_abc/files/file_123/public-urls"
58
+ @open-source="openSource"
59
+ />
60
+ </template>`;
61
+
62
+ /**
63
+ * Full card: file-type badge, title, external-link action, excerpt, and a
64
+ * page footer. Matches the Figma `citation card` frame.
65
+ */
66
+ export const Default: Story = {
67
+ parameters: { docs: { source: { code: defaultTemplateSource } } },
68
+ };
69
+
70
+ /**
71
+ * No `url` — the external-link action is hidden. Use for sources that can't
72
+ * be opened (e.g. an internal chunk with no servable file).
73
+ */
74
+ export const WithoutLink: Story = {
75
+ args: { url: undefined },
76
+ };
77
+
78
+ /**
79
+ * No `locationLabel` — the footer is hidden. Use for non-paginated sources
80
+ * such as a plain web page or a whole-document citation.
81
+ */
82
+ export const WithoutLocation: Story = {
83
+ args: { locationLabel: undefined },
84
+ };
85
+
86
+ /**
87
+ * Minimal: title + excerpt only (no link, no location). The leanest source a
88
+ * card can describe.
89
+ */
90
+ export const MinimalSnippetOnly: Story = {
91
+ args: { url: undefined, locationLabel: undefined },
92
+ };
93
+
94
+ /**
95
+ * A long file name truncates with an ellipsis (the badge and external-link
96
+ * action stay pinned, the card keeps its width); the full name stays available
97
+ * on hover via the `title` attribute. The `truncate` behaviour holds for any
98
+ * length, including a single unbroken token with no separators.
99
+ */
100
+ export const LongFileName: Story = {
101
+ args: {
102
+ fileName:
103
+ 'Annual-Strategy-And-Operational-Review-And-Lessons-Learned-Digital-Transformation-Phase-1-FY2026-Final-APPROVED-v3.pdf',
104
+ },
105
+ };
106
+
107
+ /**
108
+ * A long excerpt wraps freely — the card grows vertically while keeping its
109
+ * fixed width.
110
+ */
111
+ export const LongSnippet: Story = {
112
+ args: {
113
+ snippet: `${LOREM} ${LOREM}`,
114
+ },
115
+ };
116
+
117
+ /**
118
+ * The file-type badge derives its icon and tone from the file extension:
119
+ * `.pdf`/`.docx` → pink, `.md`/`.html` → soft, `.csv`/`.xlsx` → yellow.
120
+ */
121
+ export const FileTypeVariants: Story = {
122
+ render: (args) => ({
123
+ components: { CkCitationCard },
124
+ setup() {
125
+ const files = [
126
+ 'q3-report.pdf',
127
+ 'README.md',
128
+ 'sales-2026.csv',
129
+ 'notes.txt',
130
+ ];
131
+ return { args, files };
132
+ },
133
+ template: `
134
+ <div style="display: flex; flex-direction: column; gap: 16px;">
135
+ <CkCitationCard
136
+ v-for="f in files"
137
+ :key="f"
138
+ v-bind="args"
139
+ :file-name="f"
140
+ />
141
+ </div>
142
+ `,
143
+ }),
144
+ };
@@ -0,0 +1,76 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue';
3
+ import { UiFileType, UiButton, UiIcon } from '@aleph-alpha/ui-library';
4
+ import {
5
+ defaultChatKitLabels,
6
+ useChatKitLabels,
7
+ } from '../../../handlers/useChatKitLabels';
8
+ import type { CkCitationCardProps } from './types';
9
+
10
+ defineOptions({
11
+ name: 'CkCitationCard',
12
+ });
13
+
14
+ const props = withDefaults(defineProps<CkCitationCardProps>(), {
15
+ locationLabel: undefined,
16
+ url: undefined,
17
+ openSourceLabel: undefined,
18
+ });
19
+
20
+ const emit = defineEmits<{
21
+ 'open-source': [url: string];
22
+ }>();
23
+
24
+ const labels = useChatKitLabels();
25
+
26
+ // Resolution order: per-instance prop, injected label, English default.
27
+ const openSourceAriaLabel = computed(
28
+ () =>
29
+ props.openSourceLabel ??
30
+ labels.value.citationOpenSource ??
31
+ defaultChatKitLabels.citationOpenSource!,
32
+ );
33
+
34
+ function handleOpen(): void {
35
+ if (props.url) {
36
+ emit('open-source', props.url);
37
+ }
38
+ }
39
+ </script>
40
+
41
+ <template>
42
+ <div
43
+ class="ck-citation-card flex w-[22.5rem] max-w-[90vw] flex-col gap-2 rounded-md bg-background-surface-floating px-6 pb-6 pt-4 shadow-md"
44
+ >
45
+ <div class="flex items-center gap-3">
46
+ <UiFileType :filename="fileName" class="shrink-0" />
47
+ <p
48
+ class="min-w-0 flex-1 truncate text-sm font-medium text-content-on-surface-primary"
49
+ :title="fileName"
50
+ >
51
+ {{ fileName }}
52
+ </p>
53
+ <UiButton
54
+ v-if="url"
55
+ variant="ghost"
56
+ size="icon-sm"
57
+ :aria-label="openSourceAriaLabel"
58
+ class="shrink-0"
59
+ @click="handleOpen"
60
+ >
61
+ <UiIcon name="external-link" :size="16" aria-hidden="true" />
62
+ </UiButton>
63
+ </div>
64
+
65
+ <p class="break-words text-sm leading-5 text-content-on-surface-secondary">
66
+ {{ snippet }}
67
+ </p>
68
+
69
+ <p
70
+ v-if="locationLabel"
71
+ class="text-sm leading-5 text-content-on-surface-muted"
72
+ >
73
+ {{ locationLabel }}
74
+ </p>
75
+ </div>
76
+ </template>
@@ -0,0 +1,2 @@
1
+ export { default as CkCitationCard } from './CkCitationCard.vue';
2
+ export type * from './types';
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The floating card that describes a single citation source: the source file,
3
+ * the cited excerpt, an optional location (e.g. page), and an optional
4
+ * external-link action. Rendered as the hover/focus content of `CkCitation`,
5
+ * but usable on its own wherever a source preview is needed.
6
+ *
7
+ * Purely presentational and prop-driven. The external-link action is exposed
8
+ * as an `open-source` event rather than a plain link, because the source URL
9
+ * is often auth-proxied and cannot be navigated to directly.
10
+ *
11
+ * @category Data Display
12
+ * @useCases citation source preview, hover card for an inline reference, show the cited file/page/excerpt
13
+ * @keywords citation, source, card, popover, reference, file, page, excerpt, preview
14
+ * @related CkCitation, CkMarkdownRenderer, CkAgentMessage
15
+ */
16
+ export interface CkCitationCardProps {
17
+ /**
18
+ * Source file name. Rendered as the card title and used to derive the
19
+ * file-type badge icon/tone (pass the original name with its extension,
20
+ * e.g. `q3-report.pdf`).
21
+ */
22
+ fileName: string;
23
+ /** The cited chunk text, shown in the card body. */
24
+ snippet: string;
25
+ /**
26
+ * Location label shown in the footer, e.g. `"Page 3"`. Pass a fully
27
+ * formatted, localized string — the component does no number formatting.
28
+ * Omit for non-paginated sources to hide the footer.
29
+ */
30
+ locationLabel?: string;
31
+ /**
32
+ * When set, the external-link action is shown in the header. Activating it
33
+ * emits `open-source` with this value; the host opens it (it may be an
34
+ * auth-proxied URL that can't be a plain link). Omit to hide the action.
35
+ */
36
+ url?: string;
37
+ /**
38
+ * Accessible label for the external-link action. When omitted, resolves from
39
+ * `useChatKitLabels().citationOpenSource` (English default `"Open source"`).
40
+ * Set this to override per instance.
41
+ */
42
+ openSourceLabel?: string;
43
+ }
44
+
45
+ export interface CkCitationCardEmits {
46
+ /**
47
+ * Emitted when the user activates the header external-link action. The
48
+ * payload is `url`. Only emitted when `url` is set.
49
+ */
50
+ (e: 'open-source', url: string): void;
51
+ }
@@ -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';