@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.
- 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
- package/dist/adapters/responses-api.d.ts +11 -5
- package/dist/adapters/responses-api.d.ts.map +1 -1
- package/dist/adapters/responses-api.js +9 -3
- package/dist/chat-kit.css +14 -8
- package/dist/components/base/CkAgentMessage/CkAgentMessage.stories.d.ts +7 -0
- package/dist/components/base/CkAgentMessage/CkAgentMessage.stories.d.ts.map +1 -1
- package/dist/components/base/CkAgentMessage/types.d.ts +1 -1
- package/dist/components/base/CkCitation/CkCitation.stories.d.ts +47 -0
- package/dist/components/base/CkCitation/CkCitation.stories.d.ts.map +1 -0
- package/dist/components/base/CkCitation/CkCitation.vue.d.ts +15 -0
- package/dist/components/base/CkCitation/CkCitation.vue.d.ts.map +1 -0
- package/dist/components/base/CkCitation/index.d.ts +3 -0
- package/dist/components/base/CkCitation/index.d.ts.map +1 -0
- package/dist/components/base/CkCitation/types.d.ts +79 -0
- package/dist/components/base/CkCitation/types.d.ts.map +1 -0
- package/dist/components/base/CkCitationCard/CkCitationCard.stories.d.ts +51 -0
- package/dist/components/base/CkCitationCard/CkCitationCard.stories.d.ts.map +1 -0
- package/dist/components/base/CkCitationCard/CkCitationCard.vue.d.ts +13 -0
- package/dist/components/base/CkCitationCard/CkCitationCard.vue.d.ts.map +1 -0
- package/dist/components/base/CkCitationCard/index.d.ts +3 -0
- package/dist/components/base/CkCitationCard/index.d.ts.map +1 -0
- package/dist/components/base/CkCitationCard/types.d.ts +51 -0
- package/dist/components/base/CkCitationCard/types.d.ts.map +1 -0
- package/dist/components/base/{CkCopyMessageActionButton/CkCopyMessageActionButton.stories.d.ts → CkCopyMessageButton/CkCopyMessageButton.stories.d.ts} +3 -3
- package/dist/components/base/CkCopyMessageButton/CkCopyMessageButton.stories.d.ts.map +1 -0
- package/dist/components/base/{CkCopyMessageActionButton/CkCopyMessageActionButton.vue.d.ts → CkCopyMessageButton/CkCopyMessageButton.vue.d.ts} +4 -4
- package/dist/components/base/CkCopyMessageButton/CkCopyMessageButton.vue.d.ts.map +1 -0
- package/dist/components/base/CkCopyMessageButton/index.d.ts +3 -0
- package/dist/components/base/CkCopyMessageButton/index.d.ts.map +1 -0
- package/dist/components/base/{CkCopyMessageActionButton → CkCopyMessageButton}/types.d.ts +3 -3
- package/dist/components/base/CkCopyMessageButton/types.d.ts.map +1 -0
- package/dist/components/base/CkDropZoneOverlay/CkDropZoneOverlay.vue.d.ts +1 -1
- package/dist/components/base/CkFeedbackButton/types.d.ts +1 -1
- package/dist/components/base/CkFeedbackDialog/CkFeedbackDialog.vue.d.ts +2 -2
- package/dist/components/base/CkFeedbackDialog/types.d.ts +1 -1
- package/dist/components/base/CkFileItemsList/CkFileItemsList.vue.d.ts +1 -1
- package/dist/components/base/CkMarkdownRenderer/CkMarkdownRenderer.stories.d.ts +15 -0
- package/dist/components/base/CkMarkdownRenderer/CkMarkdownRenderer.stories.d.ts.map +1 -1
- package/dist/components/base/CkMarkdownRenderer/CkMarkdownRenderer.vue.d.ts +16 -2
- package/dist/components/base/CkMarkdownRenderer/CkMarkdownRenderer.vue.d.ts.map +1 -1
- package/dist/components/base/CkMarkdownRenderer/index.d.ts +1 -1
- package/dist/components/base/CkMarkdownRenderer/index.d.ts.map +1 -1
- package/dist/components/base/CkMarkdownRenderer/types.d.ts +25 -0
- package/dist/components/base/CkMarkdownRenderer/types.d.ts.map +1 -1
- package/dist/components/base/CkMarkdownRenderer/useCitationRendering.d.ts +17 -0
- package/dist/components/base/CkMarkdownRenderer/useCitationRendering.d.ts.map +1 -0
- package/dist/components/base/{CkMessageActionButton/CkMessageActionButton.stories.d.ts → CkMessageButton/CkMessageButton.stories.d.ts} +3 -3
- package/dist/components/base/CkMessageButton/CkMessageButton.stories.d.ts.map +1 -0
- package/dist/components/base/{CkMessageActionButton/CkMessageActionButton.vue.d.ts → CkMessageButton/CkMessageButton.vue.d.ts} +3 -3
- package/dist/components/base/CkMessageButton/CkMessageButton.vue.d.ts.map +1 -0
- package/dist/components/base/CkMessageButton/index.d.ts +3 -0
- package/dist/components/base/CkMessageButton/index.d.ts.map +1 -0
- package/dist/components/base/{CkMessageActionButton → CkMessageButton}/types.d.ts +2 -2
- package/dist/components/base/CkMessageButton/types.d.ts.map +1 -0
- package/dist/components/base/CkPromptSendButton/CkPromptSendButton.vue.d.ts +1 -1
- package/dist/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.stories.d.ts +16 -0
- package/dist/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.stories.d.ts.map +1 -0
- package/dist/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.vue.d.ts +12 -0
- package/dist/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.vue.d.ts.map +1 -0
- package/dist/components/base/CkRegenerateMessageButton/index.d.ts +3 -0
- package/dist/components/base/{CkCopyMessageActionButton → CkRegenerateMessageButton}/index.d.ts.map +1 -1
- package/dist/components/base/CkRegenerateMessageButton/types.d.ts +27 -0
- package/dist/components/base/CkRegenerateMessageButton/types.d.ts.map +1 -0
- package/dist/components/base/index.d.ts +8 -3
- package/dist/components/base/index.d.ts.map +1 -1
- package/dist/components/composed/CkConversation/CkConversation.stories.d.ts +1 -1
- package/dist/components/composed/CkConversation/types.d.ts +1 -1
- package/dist/components/composed/CkInput/CkInput.vue.d.ts +1 -1
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +26 -23
- package/dist/composables/fileUpload/useFileUpload.d.ts.map +1 -1
- package/dist/composables/fileUpload/useFileUploadWrapper.d.ts.map +1 -1
- package/dist/composables/index.js +1 -1
- package/dist/handlers/index.d.ts +2 -0
- package/dist/handlers/index.d.ts.map +1 -1
- package/dist/handlers/index.js +4 -3
- package/dist/handlers/useChatKitLabels.d.ts +13 -0
- package/dist/handlers/useChatKitLabels.d.ts.map +1 -1
- package/dist/handlers/useMarkdownParser.d.ts.map +1 -1
- package/dist/handlers/useMessageHandler.d.ts +37 -0
- package/dist/handlers/useMessageHandler.d.ts.map +1 -1
- package/dist/helpers/conversationTree.d.ts +15 -0
- package/dist/helpers/conversationTree.d.ts.map +1 -1
- package/dist/helpers/unified/remarkCitations.d.ts +17 -0
- package/dist/helpers/unified/remarkCitations.d.ts.map +1 -0
- package/dist/{index-DAmWGYNH.js → index-CyrVnbS9.js} +86 -71
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -27
- package/dist/{useChatKitLabels-D5gNv8se.js → useChatKitLabels-zMFV7aEz.js} +31 -3
- package/dist/{useMessageHandler-CMdy-lm0.js → useMessageHandler-N50Yj-NU.js} +45 -7
- package/package.json +5 -3
- package/src/adapters/responses-api.spec.ts +52 -5
- package/src/adapters/responses-api.ts +26 -7
- package/src/components/base/CkAgentMessage/CkAgentMessage.stories.ts +112 -0
- package/src/components/base/CkAgentMessage/types.ts +1 -1
- package/src/components/base/CkCitation/CkCitation.spec.ts +79 -0
- package/src/components/base/CkCitation/CkCitation.stories.ts +136 -0
- package/src/components/base/CkCitation/CkCitation.vue +145 -0
- package/src/components/base/CkCitation/index.ts +2 -0
- package/src/components/base/CkCitation/types.ts +80 -0
- package/src/components/base/CkCitationCard/CkCitationCard.spec.ts +77 -0
- package/src/components/base/CkCitationCard/CkCitationCard.stories.ts +144 -0
- package/src/components/base/CkCitationCard/CkCitationCard.vue +76 -0
- package/src/components/base/CkCitationCard/index.ts +2 -0
- package/src/components/base/CkCitationCard/types.ts +51 -0
- package/src/components/base/{CkCopyMessageActionButton/CkCopyMessageActionButton.spec.ts → CkCopyMessageButton/CkCopyMessageButton.spec.ts} +6 -6
- package/src/components/base/{CkCopyMessageActionButton/CkCopyMessageActionButton.stories.ts → CkCopyMessageButton/CkCopyMessageButton.stories.ts} +8 -8
- package/src/components/base/{CkCopyMessageActionButton/CkCopyMessageActionButton.vue → CkCopyMessageButton/CkCopyMessageButton.vue} +6 -6
- package/src/components/base/CkCopyMessageButton/index.ts +2 -0
- package/src/components/base/{CkCopyMessageActionButton → CkCopyMessageButton}/types.ts +3 -3
- package/src/components/base/CkFeedbackButton/CkFeedbackButton.vue +5 -5
- package/src/components/base/CkFeedbackButton/types.ts +1 -1
- package/src/components/base/CkFeedbackDialog/types.ts +1 -1
- package/src/components/base/CkMarkdownRenderer/CkMarkdownRenderer.spec.ts +124 -0
- package/src/components/base/CkMarkdownRenderer/CkMarkdownRenderer.stories.ts +118 -0
- package/src/components/base/CkMarkdownRenderer/CkMarkdownRenderer.vue +70 -28
- package/src/components/base/CkMarkdownRenderer/index.ts +1 -0
- package/src/components/base/CkMarkdownRenderer/types.ts +26 -0
- package/src/components/base/CkMarkdownRenderer/useCitationRendering.spec.ts +94 -0
- package/src/components/base/CkMarkdownRenderer/useCitationRendering.ts +52 -0
- package/src/components/base/{CkMessageActionButton/CkMessageActionButton.spec.ts → CkMessageButton/CkMessageButton.spec.ts} +6 -6
- package/src/components/base/{CkMessageActionButton/CkMessageActionButton.stories.ts → CkMessageButton/CkMessageButton.stories.ts} +10 -10
- package/src/components/base/{CkMessageActionButton/CkMessageActionButton.vue → CkMessageButton/CkMessageButton.vue} +3 -3
- package/src/components/base/CkMessageButton/index.ts +2 -0
- package/src/components/base/{CkMessageActionButton → CkMessageButton}/types.ts +2 -2
- package/src/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.spec.ts +49 -0
- package/src/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.stories.ts +63 -0
- package/src/components/base/CkRegenerateMessageButton/CkRegenerateMessageButton.vue +43 -0
- package/src/components/base/CkRegenerateMessageButton/index.ts +2 -0
- package/src/components/base/CkRegenerateMessageButton/types.ts +27 -0
- package/src/components/base/index.ts +15 -2
- package/src/components/composed/CkConversation/CkConversation.stories.ts +6 -6
- package/src/components/composed/CkConversation/CkConversation.vue +2 -2
- package/src/components/composed/CkConversation/types.ts +1 -1
- package/src/components/index.ts +11 -2
- package/src/composables/fileUpload/useFileUpload.spec.ts +2 -61
- package/src/composables/fileUpload/useFileUpload.ts +2 -9
- package/src/composables/fileUpload/useFileUploadWrapper.spec.ts +195 -14
- package/src/composables/fileUpload/useFileUploadWrapper.ts +114 -81
- package/src/handlers/index.ts +2 -0
- package/src/handlers/useChatKitLabels.spec.ts +1 -0
- package/src/handlers/useChatKitLabels.ts +16 -0
- package/src/handlers/useMarkdownParser.ts +5 -2
- package/src/handlers/useMessageHandler.spec.ts +79 -0
- package/src/handlers/useMessageHandler.ts +50 -2
- package/src/helpers/conversationTree.spec.ts +120 -0
- package/src/helpers/conversationTree.ts +57 -8
- package/src/helpers/unified/remarkCitations.spec.ts +112 -0
- package/src/helpers/unified/remarkCitations.ts +55 -0
- package/src/index.ts +11 -2
- package/dist/components/base/CkCopyMessageActionButton/CkCopyMessageActionButton.stories.d.ts.map +0 -1
- package/dist/components/base/CkCopyMessageActionButton/CkCopyMessageActionButton.vue.d.ts.map +0 -1
- package/dist/components/base/CkCopyMessageActionButton/index.d.ts +0 -3
- package/dist/components/base/CkCopyMessageActionButton/types.d.ts.map +0 -1
- package/dist/components/base/CkMessageActionButton/CkMessageActionButton.stories.d.ts.map +0 -1
- package/dist/components/base/CkMessageActionButton/CkMessageActionButton.vue.d.ts.map +0 -1
- package/dist/components/base/CkMessageActionButton/index.d.ts +0 -3
- package/dist/components/base/CkMessageActionButton/index.d.ts.map +0 -1
- package/dist/components/base/CkMessageActionButton/types.d.ts.map +0 -1
- package/src/components/base/CkCopyMessageActionButton/index.ts +0 -2
- package/src/components/base/CkMessageActionButton/index.ts +0 -2
|
@@ -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
|
-
*
|
|
223
|
-
*
|
|
224
|
-
* using a different attachment shape can either skip this adapter
|
|
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 = {
|
|
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
|
-
|
|
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
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import CkAgentMessage from './CkAgentMessage.vue';
|
|
2
|
+
import { CkMarkdownRenderer } from '../CkMarkdownRenderer';
|
|
3
|
+
import type { CkCitationSource } from '../CkCitation';
|
|
2
4
|
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
|
3
5
|
|
|
4
6
|
const meta: Meta<typeof CkAgentMessage> = {
|
|
@@ -213,3 +215,113 @@ export const WithoutIcon: Story = {
|
|
|
213
215
|
}),
|
|
214
216
|
parameters: { docs: { source: { code: withoutIconTemplateSource } } },
|
|
215
217
|
};
|
|
218
|
+
|
|
219
|
+
const CITATIONS: CkCitationSource[] = [
|
|
220
|
+
{
|
|
221
|
+
index: 1,
|
|
222
|
+
fileName: 'Q2_Business_Plan.pdf',
|
|
223
|
+
snippet:
|
|
224
|
+
'Departments that received customized training saw a 25% higher adoption rate of the new tools compared to those with generic training.',
|
|
225
|
+
locationLabel: 'Page 3',
|
|
226
|
+
url: '/api/v1/search-stores/ss_abc/files/file_123/public-urls',
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
index: 2,
|
|
230
|
+
fileName: 'Support-Metrics-Q1.csv',
|
|
231
|
+
snippet:
|
|
232
|
+
'Ticket volume fell 18% quarter-over-quarter for the tailored-training cohorts versus the control group.',
|
|
233
|
+
locationLabel: 'Row 42',
|
|
234
|
+
url: '/api/v1/search-stores/ss_abc/files/file_456/public-urls',
|
|
235
|
+
},
|
|
236
|
+
];
|
|
237
|
+
|
|
238
|
+
const CITED_MARKDOWN = [
|
|
239
|
+
'Based on the internal documents, customized training drove measurably better outcomes.',
|
|
240
|
+
'',
|
|
241
|
+
'Departments with tailored sessions saw a **25% higher adoption rate** [1],',
|
|
242
|
+
'and reported markedly fewer support tickets in the first quarter [2].',
|
|
243
|
+
].join('\n');
|
|
244
|
+
|
|
245
|
+
const withCitationsTemplateSource = `<script setup lang="ts">
|
|
246
|
+
import { CkAgentMessage, CkMarkdownRenderer } from '@aleph-alpha/chat-kit';
|
|
247
|
+
import type { CkCitationSource } from '@aleph-alpha/chat-kit';
|
|
248
|
+
|
|
249
|
+
// The reply body is markdown that contains inline [n] references.
|
|
250
|
+
const content = [
|
|
251
|
+
'Based on the internal documents, customized training drove measurably better outcomes.',
|
|
252
|
+
'',
|
|
253
|
+
'Departments with tailored sessions saw a **25% higher adoption rate** [1],',
|
|
254
|
+
'and reported markedly fewer support tickets in the first quarter [2].',
|
|
255
|
+
].join('\\n');
|
|
256
|
+
|
|
257
|
+
// The numbered sources, mapped from the backend citations manifest.
|
|
258
|
+
const citations: CkCitationSource[] = [
|
|
259
|
+
{
|
|
260
|
+
index: 1,
|
|
261
|
+
fileName: 'Q2_Business_Plan.pdf',
|
|
262
|
+
snippet: 'Departments that received customized training saw a 25% higher adoption rate…',
|
|
263
|
+
locationLabel: 'Page 3',
|
|
264
|
+
url: '/api/v1/search-stores/ss_abc/files/file_123/public-urls',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
index: 2,
|
|
268
|
+
fileName: 'Support-Metrics-Q1.csv',
|
|
269
|
+
snippet: 'Ticket volume fell 18% quarter-over-quarter for the tailored-training cohorts.',
|
|
270
|
+
locationLabel: 'Row 42',
|
|
271
|
+
url: '/api/v1/search-stores/ss_abc/files/file_456/public-urls',
|
|
272
|
+
},
|
|
273
|
+
];
|
|
274
|
+
|
|
275
|
+
function openSource(url: string) {
|
|
276
|
+
// Source URLs are auth-proxied; the host performs the navigation.
|
|
277
|
+
window.open(url, '_blank', 'noopener');
|
|
278
|
+
}
|
|
279
|
+
</script>
|
|
280
|
+
|
|
281
|
+
<template>
|
|
282
|
+
<CkAgentMessage>
|
|
283
|
+
<template #icon>
|
|
284
|
+
<div class="bg-core-bg-tertiary flex size-6 items-center justify-center rounded-full text-xs">
|
|
285
|
+
AI
|
|
286
|
+
</div>
|
|
287
|
+
</template>
|
|
288
|
+
<CkMarkdownRenderer
|
|
289
|
+
:content="content"
|
|
290
|
+
:citations="citations"
|
|
291
|
+
@open-source="openSource"
|
|
292
|
+
/>
|
|
293
|
+
</CkAgentMessage>
|
|
294
|
+
</template>`;
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* An assistant reply whose body is markdown containing inline `[n]` citations,
|
|
298
|
+
* rendered through `CkMarkdownRenderer` inside the message. Each `[n]` becomes
|
|
299
|
+
* an interactive `CkCitation` — hover a marker to preview its source. This is
|
|
300
|
+
* the end-to-end shape of a cited answer in a conversation.
|
|
301
|
+
*/
|
|
302
|
+
export const WithCitations: Story = {
|
|
303
|
+
render: () => ({
|
|
304
|
+
components: { CkAgentMessage, CkMarkdownRenderer },
|
|
305
|
+
setup() {
|
|
306
|
+
function openSource(url: string) {
|
|
307
|
+
window.open(url, '_blank', 'noopener');
|
|
308
|
+
}
|
|
309
|
+
return { content: CITED_MARKDOWN, citations: CITATIONS, openSource };
|
|
310
|
+
},
|
|
311
|
+
template: `
|
|
312
|
+
<CkAgentMessage>
|
|
313
|
+
<template #icon>
|
|
314
|
+
<div class="bg-core-bg-tertiary flex size-6 items-center justify-center rounded-full text-xs">
|
|
315
|
+
AI
|
|
316
|
+
</div>
|
|
317
|
+
</template>
|
|
318
|
+
<CkMarkdownRenderer
|
|
319
|
+
:content="content"
|
|
320
|
+
:citations="citations"
|
|
321
|
+
@open-source="openSource"
|
|
322
|
+
/>
|
|
323
|
+
</CkAgentMessage>
|
|
324
|
+
`,
|
|
325
|
+
}),
|
|
326
|
+
parameters: { docs: { source: { code: withCitationsTemplateSource } } },
|
|
327
|
+
};
|
|
@@ -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,
|
|
9
|
+
* @related CkUserMessage, CkConversation, CkMarkdownRenderer, CkCopyMessageButton
|
|
10
10
|
*/
|
|
11
11
|
export type CkAgentMessageProps = Record<string, never>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import CkCitation from './CkCitation.vue';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { render, screen } from '@testing-library/vue';
|
|
4
|
+
|
|
5
|
+
const base = {
|
|
6
|
+
index: 1,
|
|
7
|
+
fileName: 'q3-report.pdf',
|
|
8
|
+
snippet: 'The cited chunk text.',
|
|
9
|
+
locationLabel: 'Page 3',
|
|
10
|
+
url: '/api/v1/.../public-urls',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
describe('CkCitation', () => {
|
|
14
|
+
it('renders a marker button showing the source number', () => {
|
|
15
|
+
render(CkCitation, { props: base });
|
|
16
|
+
|
|
17
|
+
const marker = screen.getByRole('button', { name: 'Source 1' });
|
|
18
|
+
expect(marker).toHaveTextContent('1');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('derives the accessible name from the index by default', () => {
|
|
22
|
+
render(CkCitation, { props: { ...base, index: 7 } });
|
|
23
|
+
|
|
24
|
+
expect(
|
|
25
|
+
screen.getByRole('button', { name: 'Source 7' }),
|
|
26
|
+
).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('uses a custom ariaLabel when provided', () => {
|
|
30
|
+
render(CkCitation, { props: { ...base, ariaLabel: 'Quelle 1' } });
|
|
31
|
+
|
|
32
|
+
expect(
|
|
33
|
+
screen.getByRole('button', { name: 'Quelle 1' }),
|
|
34
|
+
).toBeInTheDocument();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('reveals the source card on activation and re-emits open-source', async () => {
|
|
38
|
+
const user = userEvent.setup();
|
|
39
|
+
const { emitted } = render(CkCitation, { props: base });
|
|
40
|
+
|
|
41
|
+
// The marker reveals its card on hover, matching the design.
|
|
42
|
+
await user.hover(screen.getByRole('button', { name: 'Source 1' }));
|
|
43
|
+
|
|
44
|
+
// The card content is rendered (in a popover portal) once open.
|
|
45
|
+
expect(await screen.findByText('q3-report.pdf')).toBeInTheDocument();
|
|
46
|
+
expect(screen.getByText('The cited chunk text.')).toBeInTheDocument();
|
|
47
|
+
|
|
48
|
+
await user.click(screen.getByRole('button', { name: 'Open source' }));
|
|
49
|
+
|
|
50
|
+
expect(emitted('open-source')).toEqual([['/api/v1/.../public-urls']]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('opens the card on keyboard activation of the marker', async () => {
|
|
54
|
+
const user = userEvent.setup();
|
|
55
|
+
render(CkCitation, { props: base });
|
|
56
|
+
|
|
57
|
+
const marker = screen.getByRole('button', { name: 'Source 1' });
|
|
58
|
+
marker.focus();
|
|
59
|
+
await user.keyboard('{Enter}');
|
|
60
|
+
|
|
61
|
+
expect(await screen.findByText('q3-report.pdf')).toBeInTheDocument();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('renders the default (amber) marker state by default', () => {
|
|
65
|
+
render(CkCitation, { props: base });
|
|
66
|
+
|
|
67
|
+
const marker = screen.getByRole('button', { name: 'Source 1' });
|
|
68
|
+
expect(marker.className).toContain('bg-background-accent-yellow');
|
|
69
|
+
expect(marker.className).not.toContain('bg-background-accent-soft');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('renders the selected (teal) marker state when `selected`', () => {
|
|
73
|
+
render(CkCitation, { props: { ...base, selected: true } });
|
|
74
|
+
|
|
75
|
+
const marker = screen.getByRole('button', { name: 'Source 1' });
|
|
76
|
+
expect(marker.className).toContain('bg-background-accent-soft');
|
|
77
|
+
expect(marker.className).not.toContain('bg-background-accent-yellow');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import CkCitation from './CkCitation.vue';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
|
3
|
+
|
|
4
|
+
const SNIPPET =
|
|
5
|
+
'Departments that received customized training saw a 25% higher adoption rate of the new tools compared to those with generic training.';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* CkCitation is the inline marker rendered in place of a `[n]` reference in an
|
|
9
|
+
* assistant answer — a numbered chip that reveals a `CkCitationCard` describing
|
|
10
|
+
* the source on hover or keyboard activation (Enter/Space). Drive it through `CkMarkdownRenderer`'s
|
|
11
|
+
* `citations` prop to render cited answers; use `CkCitationCard` on its own for
|
|
12
|
+
* a standalone source preview.
|
|
13
|
+
*/
|
|
14
|
+
const meta: Meta<typeof CkCitation> = {
|
|
15
|
+
title: 'Base/CkCitation',
|
|
16
|
+
component: CkCitation,
|
|
17
|
+
tags: ['autodocs'],
|
|
18
|
+
argTypes: {
|
|
19
|
+
index: { control: 'number' },
|
|
20
|
+
selected: { control: 'boolean' },
|
|
21
|
+
fileName: { control: 'text' },
|
|
22
|
+
snippet: { control: 'text' },
|
|
23
|
+
locationLabel: { control: 'text' },
|
|
24
|
+
url: { control: 'text' },
|
|
25
|
+
ariaLabel: { control: 'text' },
|
|
26
|
+
openSourceLabel: { control: 'text' },
|
|
27
|
+
},
|
|
28
|
+
args: {
|
|
29
|
+
index: 1,
|
|
30
|
+
fileName: 'Q2_Business_Plan.pdf',
|
|
31
|
+
snippet: SNIPPET,
|
|
32
|
+
locationLabel: 'Page 3',
|
|
33
|
+
url: '/api/v1/search-stores/ss_abc/files/file_123/public-urls',
|
|
34
|
+
},
|
|
35
|
+
decorators: [
|
|
36
|
+
() => ({
|
|
37
|
+
template:
|
|
38
|
+
'<div style="max-width: 640px; margin: 0 auto; padding: 96px 64px;"><story /></div>',
|
|
39
|
+
}),
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default meta;
|
|
44
|
+
type Story = StoryObj<typeof meta>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A single marker in its default (amber) state. Hover the chip (or focus it and
|
|
48
|
+
* press Enter) to reveal the source card.
|
|
49
|
+
*/
|
|
50
|
+
export const Default: Story = {};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The selected (teal) state — set `selected` when the citation's source is the
|
|
54
|
+
* one currently open/highlighted, so the active marker stands out.
|
|
55
|
+
*/
|
|
56
|
+
export const Selected: Story = {
|
|
57
|
+
args: { selected: true },
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* How markers read inside a sentence — two sources cited next to each other,
|
|
62
|
+
* the way they appear in an assistant answer.
|
|
63
|
+
*/
|
|
64
|
+
export const InlineInText: Story = {
|
|
65
|
+
render: (args) => ({
|
|
66
|
+
components: { CkCitation },
|
|
67
|
+
setup() {
|
|
68
|
+
return { args };
|
|
69
|
+
},
|
|
70
|
+
template: `
|
|
71
|
+
<p style="font-size: 14px; line-height: 20px; max-width: 32rem;">
|
|
72
|
+
Departments that received customized training saw a 25% higher adoption
|
|
73
|
+
rate of the new tools compared to those with generic training.<CkCitation
|
|
74
|
+
v-bind="args"
|
|
75
|
+
:index="1"
|
|
76
|
+
/><CkCitation
|
|
77
|
+
v-bind="args"
|
|
78
|
+
:index="2"
|
|
79
|
+
file-name="Goal-planning-26.pdf"
|
|
80
|
+
location-label="Page 12"
|
|
81
|
+
/>
|
|
82
|
+
</p>
|
|
83
|
+
`,
|
|
84
|
+
}),
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Multi-digit indices keep the chip legible — the marker grows with its
|
|
89
|
+
* number while staying aligned to the surrounding text.
|
|
90
|
+
*/
|
|
91
|
+
export const MultiDigitIndex: Story = {
|
|
92
|
+
args: { index: 12 },
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* No `url` — the card still appears on hover but without the external-link
|
|
97
|
+
* action (e.g. an internal chunk with no servable file).
|
|
98
|
+
*/
|
|
99
|
+
export const WithoutLink: Story = {
|
|
100
|
+
args: { url: undefined },
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Non-paginated source — the card omits the page footer.
|
|
105
|
+
*/
|
|
106
|
+
export const WithoutLocation: Story = {
|
|
107
|
+
args: { locationLabel: undefined },
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* A row of markers with different file types — the card's badge tone follows
|
|
112
|
+
* each source's extension.
|
|
113
|
+
*/
|
|
114
|
+
export const SourceTypes: Story = {
|
|
115
|
+
render: () => ({
|
|
116
|
+
components: { CkCitation },
|
|
117
|
+
setup() {
|
|
118
|
+
const sources = [
|
|
119
|
+
{ index: 1, fileName: 'q3-report.pdf', locationLabel: 'Page 3' },
|
|
120
|
+
{ index: 2, fileName: 'README.md' },
|
|
121
|
+
{ index: 3, fileName: 'sales-2026.csv', locationLabel: 'Row 42' },
|
|
122
|
+
];
|
|
123
|
+
return { sources, snippet: SNIPPET };
|
|
124
|
+
},
|
|
125
|
+
template: `
|
|
126
|
+
<p style="font-size: 14px; line-height: 20px;">
|
|
127
|
+
Multiple sources back this claim.<CkCitation
|
|
128
|
+
v-for="s in sources"
|
|
129
|
+
:key="s.index"
|
|
130
|
+
v-bind="s"
|
|
131
|
+
:snippet="snippet"
|
|
132
|
+
/>
|
|
133
|
+
</p>
|
|
134
|
+
`,
|
|
135
|
+
}),
|
|
136
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, onBeforeUnmount, ref } from 'vue';
|
|
3
|
+
import {
|
|
4
|
+
UiPopover,
|
|
5
|
+
UiPopoverTrigger,
|
|
6
|
+
UiPopoverContent,
|
|
7
|
+
} from '@aleph-alpha/ui-library';
|
|
8
|
+
import {
|
|
9
|
+
defaultChatKitLabels,
|
|
10
|
+
useChatKitLabels,
|
|
11
|
+
} from '../../../handlers/useChatKitLabels';
|
|
12
|
+
import { CkCitationCard } from '../CkCitationCard';
|
|
13
|
+
import type { CkCitationProps } from './types';
|
|
14
|
+
|
|
15
|
+
defineOptions({
|
|
16
|
+
name: 'CkCitation',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const props = withDefaults(defineProps<CkCitationProps>(), {
|
|
20
|
+
selected: false,
|
|
21
|
+
locationLabel: undefined,
|
|
22
|
+
url: undefined,
|
|
23
|
+
ariaLabel: undefined,
|
|
24
|
+
openSourceLabel: undefined,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const emit = defineEmits<{
|
|
28
|
+
'open-source': [url: string];
|
|
29
|
+
}>();
|
|
30
|
+
|
|
31
|
+
const labels = useChatKitLabels();
|
|
32
|
+
|
|
33
|
+
// Resolution order: per-instance prop, then `"{citationSourceLabel} {index}"`
|
|
34
|
+
// from the injected label, then the English default.
|
|
35
|
+
const triggerLabel = computed(
|
|
36
|
+
() =>
|
|
37
|
+
props.ariaLabel ??
|
|
38
|
+
`${labels.value.citationSourceLabel ?? defaultChatKitLabels.citationSourceLabel!} ${props.index}`,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// Close delay so the pointer can cross the gap from the marker into the card
|
|
42
|
+
// without it disappearing on the way.
|
|
43
|
+
const CLOSE_DELAY_MS = 80;
|
|
44
|
+
|
|
45
|
+
const open = ref(false);
|
|
46
|
+
// Tracks whether the current open was driven by the pointer (hover) rather than
|
|
47
|
+
// the keyboard, so we can suppress the popover's focus management for hover only.
|
|
48
|
+
const isPointerInteraction = ref(false);
|
|
49
|
+
let closeTimer: ReturnType<typeof setTimeout> | undefined;
|
|
50
|
+
|
|
51
|
+
function show(): void {
|
|
52
|
+
if (closeTimer) {
|
|
53
|
+
clearTimeout(closeTimer);
|
|
54
|
+
closeTimer = undefined;
|
|
55
|
+
}
|
|
56
|
+
open.value = true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function scheduleHide(): void {
|
|
60
|
+
if (closeTimer) clearTimeout(closeTimer);
|
|
61
|
+
closeTimer = setTimeout(() => {
|
|
62
|
+
open.value = false;
|
|
63
|
+
closeTimer = undefined;
|
|
64
|
+
}, CLOSE_DELAY_MS);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function onPointerEnter(): void {
|
|
68
|
+
isPointerInteraction.value = true;
|
|
69
|
+
show();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function onPointerLeave(): void {
|
|
73
|
+
isPointerInteraction.value = false;
|
|
74
|
+
scheduleHide();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Suppress auto-focus for hover opens only (so hovering never steals focus or
|
|
78
|
+
// scrolls). Keyboard opens keep it, moving focus into the card so the source
|
|
79
|
+
// action is reachable.
|
|
80
|
+
function onOpenAutoFocus(event: Event): void {
|
|
81
|
+
if (isPointerInteraction.value) event.preventDefault();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Close once focus leaves the card entirely (keyboard users tabbing out). Moving
|
|
85
|
+
// focus between elements inside the card keeps it open.
|
|
86
|
+
function onContentFocusOut(event: FocusEvent): void {
|
|
87
|
+
const nextTarget = event.relatedTarget as Node | null;
|
|
88
|
+
const content = event.currentTarget as HTMLElement;
|
|
89
|
+
if (!nextTarget || !content.contains(nextTarget)) {
|
|
90
|
+
scheduleHide();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
onBeforeUnmount(() => {
|
|
95
|
+
if (closeTimer) clearTimeout(closeTimer);
|
|
96
|
+
});
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<template>
|
|
100
|
+
<UiPopover v-model:open="open">
|
|
101
|
+
<UiPopoverTrigger as-child>
|
|
102
|
+
<button
|
|
103
|
+
type="button"
|
|
104
|
+
:aria-label="triggerLabel"
|
|
105
|
+
:class="[
|
|
106
|
+
'ck-citation mx-px inline-flex h-5 min-w-5 select-none items-center justify-center rounded-sm px-1 align-middle text-sm leading-4 tabular-nums transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
|
107
|
+
selected
|
|
108
|
+
? 'bg-background-accent-soft text-black'
|
|
109
|
+
: 'bg-background-accent-yellow text-background-status-warning',
|
|
110
|
+
]"
|
|
111
|
+
@mouseenter="onPointerEnter"
|
|
112
|
+
@mouseleave="onPointerLeave"
|
|
113
|
+
>
|
|
114
|
+
{{ index }}
|
|
115
|
+
</button>
|
|
116
|
+
</UiPopoverTrigger>
|
|
117
|
+
<UiPopoverContent
|
|
118
|
+
align="start"
|
|
119
|
+
:side-offset="8"
|
|
120
|
+
class="w-auto border-0 bg-transparent p-0 shadow-none"
|
|
121
|
+
@open-auto-focus="onOpenAutoFocus"
|
|
122
|
+
@mouseenter="onPointerEnter"
|
|
123
|
+
@mouseleave="onPointerLeave"
|
|
124
|
+
@focusin="show"
|
|
125
|
+
@focusout="onContentFocusOut"
|
|
126
|
+
>
|
|
127
|
+
<CkCitationCard
|
|
128
|
+
:file-name="fileName"
|
|
129
|
+
:snippet="snippet"
|
|
130
|
+
:location-label="locationLabel"
|
|
131
|
+
:url="url"
|
|
132
|
+
:open-source-label="openSourceLabel"
|
|
133
|
+
@open-source="(u: string) => emit('open-source', u)"
|
|
134
|
+
/>
|
|
135
|
+
</UiPopoverContent>
|
|
136
|
+
</UiPopover>
|
|
137
|
+
</template>
|
|
138
|
+
|
|
139
|
+
<style scoped>
|
|
140
|
+
@media (prefers-reduced-motion: reduce) {
|
|
141
|
+
.ck-citation {
|
|
142
|
+
transition-duration: 0.01ms;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
</style>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single citation source: the data needed to render a marker and its card.
|
|
3
|
+
* This is the per-source shape the host maps each backend citation into (the
|
|
4
|
+
* `index` matches the `[n]` reference in the answer text) and the element type
|
|
5
|
+
* of `CkMarkdownRenderer`'s `citations` prop.
|
|
6
|
+
*
|
|
7
|
+
* @category Data Display
|
|
8
|
+
* @useCases describe a cited source, build the citations map for an assistant reply
|
|
9
|
+
* @keywords citation, source, reference, file, page, snippet
|
|
10
|
+
* @related CkCitation, CkCitationCard, CkMarkdownRenderer
|
|
11
|
+
*/
|
|
12
|
+
export interface CkCitationSource {
|
|
13
|
+
/**
|
|
14
|
+
* 1-based source number rendered in the marker. Matches the `[n]`
|
|
15
|
+
* reference it replaces in the answer text.
|
|
16
|
+
*/
|
|
17
|
+
index: number;
|
|
18
|
+
/**
|
|
19
|
+
* Source file name. Title of the card and basis for the file-type badge
|
|
20
|
+
* (pass the original name with its extension).
|
|
21
|
+
*/
|
|
22
|
+
fileName: string;
|
|
23
|
+
/** The cited chunk text, shown in the card body. */
|
|
24
|
+
snippet: string;
|
|
25
|
+
/**
|
|
26
|
+
* Location label for the card footer, e.g. `"Page 3"`. Omit for
|
|
27
|
+
* non-paginated sources.
|
|
28
|
+
*/
|
|
29
|
+
locationLabel?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Source URL for the card's external-link action. Activating it emits
|
|
32
|
+
* `open-source`. Omit to hide the action.
|
|
33
|
+
*/
|
|
34
|
+
url?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* An inline citation marker — the numbered chip rendered in place of a `[n]`
|
|
39
|
+
* reference inside an assistant answer. On hover, or when activated by
|
|
40
|
+
* keyboard (Enter/Space) or click, it reveals a `CkCitationCard` describing
|
|
41
|
+
* the source.
|
|
42
|
+
*
|
|
43
|
+
* The marker shows the bare source number (no brackets). Activating the
|
|
44
|
+
* card's external-link action re-emits `open-source` so the host can open
|
|
45
|
+
* the (possibly auth-proxied) source URL.
|
|
46
|
+
*
|
|
47
|
+
* @category Data Display
|
|
48
|
+
* @useCases inline citation in assistant reply, numbered source reference, hover to preview the cited file/page
|
|
49
|
+
* @keywords citation, source, reference, inline, marker, footnote, number, hover, popover
|
|
50
|
+
* @related CkCitationCard, CkMarkdownRenderer, CkAgentMessage
|
|
51
|
+
*/
|
|
52
|
+
export interface CkCitationProps extends CkCitationSource {
|
|
53
|
+
/**
|
|
54
|
+
* Marks the citation as selected/active (e.g. the source it points to is
|
|
55
|
+
* currently open or highlighted). Renders the marker in its selected (teal)
|
|
56
|
+
* state instead of the default amber.
|
|
57
|
+
* @default false
|
|
58
|
+
*/
|
|
59
|
+
selected?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Accessible label for the marker trigger. When omitted, resolves to
|
|
62
|
+
* `"{citationSourceLabel} {index}"` from `useChatKitLabels` (English default
|
|
63
|
+
* `"Source {index}"`). Set this to override per instance.
|
|
64
|
+
*/
|
|
65
|
+
ariaLabel?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Accessible label for the card's external-link action. When omitted,
|
|
68
|
+
* resolves from `useChatKitLabels().citationOpenSource` (English default
|
|
69
|
+
* `"Open source"`).
|
|
70
|
+
*/
|
|
71
|
+
openSourceLabel?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface CkCitationEmits {
|
|
75
|
+
/**
|
|
76
|
+
* Re-emitted from the card when the user opens the source. The payload is
|
|
77
|
+
* `url`.
|
|
78
|
+
*/
|
|
79
|
+
(e: 'open-source', url: string): void;
|
|
80
|
+
}
|