@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { render, screen } from '@testing-library/vue';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
2
3
|
import { h } from 'vue';
|
|
3
4
|
import CkMarkdownRenderer from './CkMarkdownRenderer.vue';
|
|
4
5
|
import { defaultMarkdownClasses } from './defaultMarkdownClasses';
|
|
@@ -345,4 +346,127 @@ describe('CkMarkdownRenderer', () => {
|
|
|
345
346
|
expect(container.querySelector('p')?.textContent).toBe('PREFIX:HELLO');
|
|
346
347
|
});
|
|
347
348
|
});
|
|
349
|
+
|
|
350
|
+
describe('citations', () => {
|
|
351
|
+
const citations = [
|
|
352
|
+
{
|
|
353
|
+
index: 1,
|
|
354
|
+
fileName: 'q3-report.pdf',
|
|
355
|
+
snippet: 'the cited chunk text',
|
|
356
|
+
locationLabel: 'Page 3',
|
|
357
|
+
url: '/api/v1/.../public-urls',
|
|
358
|
+
},
|
|
359
|
+
];
|
|
360
|
+
|
|
361
|
+
it('still parses markdown and renders [n] as an interactive citation', () => {
|
|
362
|
+
const { container } = render(CkMarkdownRenderer, {
|
|
363
|
+
props: { content: 'Adoption rose **25%** [1].', citations },
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// Markdown is parsed (bold becomes <strong>, not literal asterisks)...
|
|
367
|
+
expect(container.querySelector('strong')?.textContent).toBe('25%');
|
|
368
|
+
expect(container.textContent).not.toContain('**');
|
|
369
|
+
// ...and [1] becomes a citation marker.
|
|
370
|
+
expect(
|
|
371
|
+
screen.getByRole('button', { name: 'Source 1' }),
|
|
372
|
+
).toBeInTheDocument();
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it('leaves an unsupported [n] (no matching source) as plain text', () => {
|
|
376
|
+
const { container } = render(CkMarkdownRenderer, {
|
|
377
|
+
props: { content: 'No source for this [9].', citations },
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
expect(container.textContent).toContain('[9]');
|
|
381
|
+
expect(screen.queryByRole('button', { name: 'Source 9' })).toBeNull();
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('emits open-source end-to-end when a citation card action is used', async () => {
|
|
385
|
+
const user = userEvent.setup();
|
|
386
|
+
const { emitted } = render(CkMarkdownRenderer, {
|
|
387
|
+
props: { content: 'See the report [1].', citations },
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
await user.hover(screen.getByRole('button', { name: 'Source 1' }));
|
|
391
|
+
await user.click(
|
|
392
|
+
await screen.findByRole('button', { name: 'Open source' }),
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
expect(emitted('open-source')).toEqual([['/api/v1/.../public-urls']]);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it('renders the `citation` slot instead of the default marker when provided', async () => {
|
|
399
|
+
const user = userEvent.setup();
|
|
400
|
+
const { emitted } = render(CkMarkdownRenderer, {
|
|
401
|
+
props: { content: 'See the report [1].', citations },
|
|
402
|
+
slots: {
|
|
403
|
+
citation: ({
|
|
404
|
+
source,
|
|
405
|
+
openSource,
|
|
406
|
+
}: {
|
|
407
|
+
source: { index: number; url?: string };
|
|
408
|
+
openSource: (url: string) => void;
|
|
409
|
+
}) =>
|
|
410
|
+
h(
|
|
411
|
+
'button',
|
|
412
|
+
{ onClick: () => source.url && openSource(source.url) },
|
|
413
|
+
`custom-${source.index}`,
|
|
414
|
+
),
|
|
415
|
+
},
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
const custom = screen.getByRole('button', { name: 'custom-1' });
|
|
419
|
+
expect(custom).toBeInTheDocument();
|
|
420
|
+
// The default chip is replaced, not added alongside.
|
|
421
|
+
expect(screen.queryByRole('button', { name: 'Source 1' })).toBeNull();
|
|
422
|
+
|
|
423
|
+
await user.click(custom);
|
|
424
|
+
expect(emitted('open-source')).toEqual([['/api/v1/.../public-urls']]);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
describe('numeric marker conversion across markdown contexts', () => {
|
|
428
|
+
it('does not convert [n] inside an inline code span', () => {
|
|
429
|
+
const { container } = render(CkMarkdownRenderer, {
|
|
430
|
+
props: { content: 'use `arr[1]` here', citations },
|
|
431
|
+
});
|
|
432
|
+
expect(container.querySelector('code')?.textContent).toBe('arr[1]');
|
|
433
|
+
expect(screen.queryByRole('button', { name: 'Source 1' })).toBeNull();
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it('does not convert [n] inside a fenced code block', () => {
|
|
437
|
+
const { container } = render(CkMarkdownRenderer, {
|
|
438
|
+
props: { content: '```\n[1]\n```', citations },
|
|
439
|
+
});
|
|
440
|
+
expect(container.querySelector('pre')?.textContent).toContain('[1]');
|
|
441
|
+
expect(screen.queryByRole('button', { name: 'Source 1' })).toBeNull();
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('does not convert a non-numeric [note] marker', () => {
|
|
445
|
+
const { container } = render(CkMarkdownRenderer, {
|
|
446
|
+
props: { content: 'see [note] here', citations },
|
|
447
|
+
});
|
|
448
|
+
expect(container.textContent).toContain('[note]');
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('converts a marker at the very start of the content', () => {
|
|
452
|
+
render(CkMarkdownRenderer, {
|
|
453
|
+
props: { content: '[1] leads the paragraph', citations },
|
|
454
|
+
});
|
|
455
|
+
expect(
|
|
456
|
+
screen.getByRole('button', { name: 'Source 1' }),
|
|
457
|
+
).toBeInTheDocument();
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it('converts markers inside a heading and a list item', () => {
|
|
461
|
+
const { container } = render(CkMarkdownRenderer, {
|
|
462
|
+
props: { content: '# Title [1]\n\n- item [1]', citations },
|
|
463
|
+
});
|
|
464
|
+
expect(container.querySelector('h1')).toBeInTheDocument();
|
|
465
|
+
expect(container.querySelector('li')).toBeInTheDocument();
|
|
466
|
+
expect(
|
|
467
|
+
screen.getAllByRole('button', { name: 'Source 1' }),
|
|
468
|
+
).toHaveLength(2);
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
});
|
|
348
472
|
});
|
|
@@ -430,3 +430,121 @@ export const WithCustomPlugin: Story = {
|
|
|
430
430
|
}),
|
|
431
431
|
parameters: { docs: { source: { code: withCustomPluginTemplateSource } } },
|
|
432
432
|
};
|
|
433
|
+
|
|
434
|
+
const withCitationsTemplateSource = `<script setup lang="ts">
|
|
435
|
+
import { CkMarkdownRenderer } from '@aleph-alpha/chat-kit';
|
|
436
|
+
import type { CkCitationSource } from '@aleph-alpha/chat-kit';
|
|
437
|
+
|
|
438
|
+
// An assistant reply is markdown that may contain inline [n] references.
|
|
439
|
+
const content = [
|
|
440
|
+
'Customized training drove measurably better outcomes.',
|
|
441
|
+
'',
|
|
442
|
+
'Departments with tailored sessions saw a **25% higher adoption rate** [1],',
|
|
443
|
+
'and reported fewer support tickets in the first quarter [2].',
|
|
444
|
+
].join('\\n');
|
|
445
|
+
|
|
446
|
+
// The numbered sources, mapped from the backend citations manifest.
|
|
447
|
+
const citations: CkCitationSource[] = [
|
|
448
|
+
{
|
|
449
|
+
index: 1,
|
|
450
|
+
fileName: 'Q2_Business_Plan.pdf',
|
|
451
|
+
snippet: 'Departments that received customized training saw a 25% higher adoption rate…',
|
|
452
|
+
locationLabel: 'Page 3',
|
|
453
|
+
url: '/api/v1/search-stores/ss_abc/files/file_123/public-urls',
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
index: 2,
|
|
457
|
+
fileName: 'Support-Metrics-Q1.csv',
|
|
458
|
+
snippet: 'Ticket volume fell 18% quarter-over-quarter for tailored-training cohorts.',
|
|
459
|
+
locationLabel: 'Row 42',
|
|
460
|
+
url: '/api/v1/search-stores/ss_abc/files/file_456/public-urls',
|
|
461
|
+
},
|
|
462
|
+
];
|
|
463
|
+
|
|
464
|
+
function openSource(url: string) {
|
|
465
|
+
// Source URLs are auth-proxied; the host performs the navigation.
|
|
466
|
+
window.open(url, '_blank', 'noopener');
|
|
467
|
+
}
|
|
468
|
+
</script>
|
|
469
|
+
|
|
470
|
+
<template>
|
|
471
|
+
<CkMarkdownRenderer
|
|
472
|
+
:content="content"
|
|
473
|
+
:citations="citations"
|
|
474
|
+
@open-source="openSource"
|
|
475
|
+
/>
|
|
476
|
+
</template>`;
|
|
477
|
+
|
|
478
|
+
const CITATIONS = [
|
|
479
|
+
{
|
|
480
|
+
index: 1,
|
|
481
|
+
fileName: 'Q2_Business_Plan.pdf',
|
|
482
|
+
snippet:
|
|
483
|
+
'Departments that received customized training saw a 25% higher adoption rate of the new tools compared to those with generic training.',
|
|
484
|
+
locationLabel: 'Page 3',
|
|
485
|
+
url: '/api/v1/search-stores/ss_abc/files/file_123/public-urls',
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
index: 2,
|
|
489
|
+
fileName: 'Support-Metrics-Q1.csv',
|
|
490
|
+
snippet:
|
|
491
|
+
'Ticket volume fell 18% quarter-over-quarter for the tailored-training cohorts versus the control group.',
|
|
492
|
+
locationLabel: 'Row 42',
|
|
493
|
+
url: '/api/v1/search-stores/ss_abc/files/file_456/public-urls',
|
|
494
|
+
},
|
|
495
|
+
];
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* The headline integration: an assistant reply is **markdown that contains
|
|
499
|
+
* inline `[n]` references**, and the `citations` prop turns each one into an
|
|
500
|
+
* interactive `CkCitation` (numbered marker + hover card) — handled as a
|
|
501
|
+
* dedicated case, distinct from ordinary markdown. Hover a marker to preview
|
|
502
|
+
* its source; the external-link action emits `open-source`.
|
|
503
|
+
*/
|
|
504
|
+
export const WithCitations: Story = {
|
|
505
|
+
render: () => ({
|
|
506
|
+
components: { CkMarkdownRenderer },
|
|
507
|
+
setup() {
|
|
508
|
+
const content = [
|
|
509
|
+
'## Training impact',
|
|
510
|
+
'',
|
|
511
|
+
'Customized training drove measurably better outcomes across teams.',
|
|
512
|
+
'',
|
|
513
|
+
'Departments with tailored sessions saw a **25% higher adoption rate** [1],',
|
|
514
|
+
'and reported markedly fewer support tickets in the first quarter [2].',
|
|
515
|
+
'',
|
|
516
|
+
'- Faster onboarding for new hires [1]',
|
|
517
|
+
'- Lower support load [2]',
|
|
518
|
+
].join('\n');
|
|
519
|
+
function openSource(url: string) {
|
|
520
|
+
window.open(url, '_blank', 'noopener');
|
|
521
|
+
}
|
|
522
|
+
return { content, citations: CITATIONS, openSource };
|
|
523
|
+
},
|
|
524
|
+
template:
|
|
525
|
+
'<CkMarkdownRenderer :content="content" :citations="citations" @open-source="openSource" />',
|
|
526
|
+
}),
|
|
527
|
+
parameters: { docs: { source: { code: withCitationsTemplateSource } } },
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Unsupported markers degrade gracefully: a `[n]` with no matching source
|
|
532
|
+
* (here `[9]`) is left as plain text rather than rendered as a citation, while
|
|
533
|
+
* the markers that do have sources still render. Citation handling never
|
|
534
|
+
* touches code spans — the `[3]` inside `` `arr[3]` `` stays literal.
|
|
535
|
+
*/
|
|
536
|
+
export const WithUnsupportedAndCodeMarkers: Story = {
|
|
537
|
+
render: () => ({
|
|
538
|
+
components: { CkMarkdownRenderer },
|
|
539
|
+
setup() {
|
|
540
|
+
const content = [
|
|
541
|
+
'Two real sources back this [1][2], but [9] has no source and stays as text.',
|
|
542
|
+
'',
|
|
543
|
+
'A marker inside code such as `arr[3]` is never treated as a citation.',
|
|
544
|
+
].join('\n');
|
|
545
|
+
return { content, citations: CITATIONS };
|
|
546
|
+
},
|
|
547
|
+
template:
|
|
548
|
+
'<CkMarkdownRenderer :content="content" :citations="citations" />',
|
|
549
|
+
}),
|
|
550
|
+
};
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
defaultChatKitLabels,
|
|
15
15
|
useChatKitLabels,
|
|
16
16
|
} from '../../../handlers/useChatKitLabels';
|
|
17
|
+
import { useCitationRendering } from './useCitationRendering';
|
|
18
|
+
import type { CkCitationSource } from '../CkCitation';
|
|
17
19
|
import type { CkMarkdownElementClasses } from './types';
|
|
18
20
|
import { defaultMarkdownClasses } from './defaultMarkdownClasses';
|
|
19
21
|
|
|
@@ -21,30 +23,44 @@ import { defaultMarkdownClasses } from './defaultMarkdownClasses';
|
|
|
21
23
|
// never forces the whole message column wider on narrow viewports.
|
|
22
24
|
const TABLE_WRAPPER_CLASS = 'max-w-full overflow-x-auto';
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
type RenderedNode = VNode | VNode[] | string | null;
|
|
27
|
+
|
|
28
|
+
interface DomToVNodeContext {
|
|
29
|
+
slots: Slots;
|
|
30
|
+
classes: CkMarkdownElementClasses;
|
|
31
|
+
tableLabel: string;
|
|
32
|
+
// Maps a citation placeholder element to a node, or null for other elements.
|
|
33
|
+
renderCitation: (el: Element) => VNode | VNode[] | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Routes a fenced code block to the `code-block` slot when one is provided;
|
|
37
|
+
// otherwise returns null so it renders as the default `<pre><code>`.
|
|
38
|
+
function renderCodeBlock(el: Element, ctx: DomToVNodeContext): VNode[] | null {
|
|
39
|
+
if (el.tagName !== 'PRE' || el.firstElementChild?.tagName !== 'CODE') {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
if (!ctx.slots['code-block']) return null;
|
|
43
|
+
|
|
44
|
+
const codeEl = el.firstElementChild;
|
|
45
|
+
const language = codeEl.className?.match(/language-(\S+)/)?.[1] ?? '';
|
|
46
|
+
const code = codeEl.textContent ?? '';
|
|
47
|
+
return ctx.slots['code-block']({ code, language });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function domToVNode(node: Node, ctx: DomToVNodeContext): RenderedNode {
|
|
30
51
|
if (node.nodeType === Node.TEXT_NODE) return node.textContent;
|
|
31
52
|
if (node.nodeType !== Node.ELEMENT_NODE) return null;
|
|
32
53
|
|
|
33
54
|
const el = node as Element;
|
|
34
55
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (slots['code-block']) {
|
|
41
|
-
return slots['code-block']({ code, language }) as unknown as VNode;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
56
|
+
// Explicit null check: a slot may legitimately render an empty string, which
|
|
57
|
+
// must still count as "handled" rather than falling through to the element.
|
|
58
|
+
const special = renderCodeBlock(el, ctx) ?? ctx.renderCitation(el);
|
|
59
|
+
if (special != null) return special;
|
|
44
60
|
|
|
45
61
|
const children = Array.from(node.childNodes)
|
|
46
|
-
.map((child) => domToVNode(child,
|
|
47
|
-
.filter((c): c is
|
|
62
|
+
.map((child) => domToVNode(child, ctx))
|
|
63
|
+
.filter((c): c is NonNullable<RenderedNode> => c != null);
|
|
48
64
|
|
|
49
65
|
const attrs: Record<string, string> = {};
|
|
50
66
|
for (const attr of el.attributes) {
|
|
@@ -52,7 +68,7 @@ function domToVNode(
|
|
|
52
68
|
}
|
|
53
69
|
|
|
54
70
|
const tag = el.tagName.toLowerCase();
|
|
55
|
-
const customClass = classes[tag as keyof CkMarkdownElementClasses];
|
|
71
|
+
const customClass = ctx.classes[tag as keyof CkMarkdownElementClasses];
|
|
56
72
|
if (customClass) {
|
|
57
73
|
attrs.class = attrs.class ? `${attrs.class} ${customClass}` : customClass;
|
|
58
74
|
}
|
|
@@ -66,7 +82,7 @@ function domToVNode(
|
|
|
66
82
|
class: TABLE_WRAPPER_CLASS,
|
|
67
83
|
tabindex: '0',
|
|
68
84
|
role: 'region',
|
|
69
|
-
'aria-label': tableLabel,
|
|
85
|
+
'aria-label': ctx.tableLabel,
|
|
70
86
|
},
|
|
71
87
|
[vnode],
|
|
72
88
|
);
|
|
@@ -84,12 +100,34 @@ export default defineComponent({
|
|
|
84
100
|
default: () => ({}),
|
|
85
101
|
},
|
|
86
102
|
plugins: { type: Array as PropType<PluggableList>, default: undefined },
|
|
103
|
+
citations: {
|
|
104
|
+
type: Array as PropType<CkCitationSource[]>,
|
|
105
|
+
default: undefined,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
emits: {
|
|
109
|
+
'open-source': (url: string) => typeof url === 'string',
|
|
87
110
|
},
|
|
88
|
-
setup(props, { slots }) {
|
|
89
|
-
const { content
|
|
90
|
-
const { html } = useMarkdownParser(content, plugins);
|
|
111
|
+
setup(props, { slots, emit }) {
|
|
112
|
+
const { content } = toRefs(props);
|
|
91
113
|
const labels = useChatKitLabels();
|
|
92
114
|
|
|
115
|
+
function handleOpenSource(url: string): void {
|
|
116
|
+
emit('open-source', url);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const { citationPlugins, renderCitationNode } = useCitationRendering(
|
|
120
|
+
() => props.citations,
|
|
121
|
+
handleOpenSource,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const effectivePlugins = computed<PluggableList>(() => [
|
|
125
|
+
...(props.plugins ?? []),
|
|
126
|
+
...citationPlugins.value,
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
const { html } = useMarkdownParser(content, effectivePlugins);
|
|
130
|
+
|
|
93
131
|
const parsedNodes = computed(() => {
|
|
94
132
|
const raw = html.value;
|
|
95
133
|
if (!raw) return [];
|
|
@@ -103,13 +141,17 @@ export default defineComponent({
|
|
|
103
141
|
}));
|
|
104
142
|
|
|
105
143
|
return () => {
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
144
|
+
const ctx: DomToVNodeContext = {
|
|
145
|
+
slots,
|
|
146
|
+
classes: mergedClasses.value,
|
|
147
|
+
tableLabel:
|
|
148
|
+
labels.value.markdownTableLabel ??
|
|
149
|
+
defaultChatKitLabels.markdownTableLabel!,
|
|
150
|
+
renderCitation: (el) => renderCitationNode(el, slots.citation),
|
|
151
|
+
};
|
|
110
152
|
const children = parsedNodes.value
|
|
111
|
-
.map((node) => domToVNode(node,
|
|
112
|
-
.filter((c): c is
|
|
153
|
+
.map((node) => domToVNode(node, ctx))
|
|
154
|
+
.filter((c): c is NonNullable<RenderedNode> => c != null);
|
|
113
155
|
return h(
|
|
114
156
|
'div',
|
|
115
157
|
{ class: 'markdown-renderer min-w-0 break-words' },
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PluggableList } from 'unified';
|
|
2
|
+
import type { CkCitationSource } from '../CkCitation';
|
|
2
3
|
|
|
3
4
|
export interface CkMarkdownElementClasses {
|
|
4
5
|
h1?: string;
|
|
@@ -50,4 +51,29 @@ export interface CkMarkdownRendererProps {
|
|
|
50
51
|
classes?: CkMarkdownElementClasses;
|
|
51
52
|
/** Extra [unified](https://unifiedjs.com/) plugins to extend parsing. */
|
|
52
53
|
plugins?: PluggableList;
|
|
54
|
+
/**
|
|
55
|
+
* Citation sources for inline `[n]` references in `content`. When provided,
|
|
56
|
+
* each `[n]` whose number matches a source's `index` is rendered as an
|
|
57
|
+
* interactive `CkCitation` (numbered marker + hover card); unmatched markers
|
|
58
|
+
* stay as plain text. Fully opt-in — omit it and markdown renders unchanged.
|
|
59
|
+
*
|
|
60
|
+
* Notes for callers:
|
|
61
|
+
* - Any numeric `[n]` in prose is turned into a citation when source `n`
|
|
62
|
+
* exists — including incidental ones like array indexing (`arr[1]`). Wrap
|
|
63
|
+
* such text in a code span (`` `arr[1]` ``) to keep it literal. `[n]` inside
|
|
64
|
+
* code spans/blocks is never converted.
|
|
65
|
+
* - `index` must be unique across sources; on a collision the last wins.
|
|
66
|
+
* - Pass a stable/memoized array reference. A new array identity on every
|
|
67
|
+
* render forces the markdown processor to rebuild.
|
|
68
|
+
*/
|
|
69
|
+
citations?: CkCitationSource[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface CkMarkdownRendererEmits {
|
|
73
|
+
/**
|
|
74
|
+
* Emitted when the user opens a citation's source from its card. The
|
|
75
|
+
* payload is the source `url`. Forward it to a handler that performs the
|
|
76
|
+
* (often auth-proxied) navigation.
|
|
77
|
+
*/
|
|
78
|
+
(e: 'open-source', url: string): void;
|
|
53
79
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { h, type VNode } from 'vue';
|
|
2
|
+
import { useCitationRendering } from './useCitationRendering';
|
|
3
|
+
import { CkCitation } from '../CkCitation';
|
|
4
|
+
import { remarkCitations } from '../../../helpers/unified/remarkCitations';
|
|
5
|
+
import type { CkCitationSource } from '../CkCitation';
|
|
6
|
+
|
|
7
|
+
const SOURCES: CkCitationSource[] = [
|
|
8
|
+
{
|
|
9
|
+
index: 1,
|
|
10
|
+
fileName: 'q3-report.pdf',
|
|
11
|
+
snippet: 'the cited chunk text',
|
|
12
|
+
locationLabel: 'Page 3',
|
|
13
|
+
url: '/api/v1/.../public-urls',
|
|
14
|
+
},
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
function citationPlaceholder(index: number): Element {
|
|
18
|
+
const el = document.createElement('span');
|
|
19
|
+
el.setAttribute('data-citation-index', String(index));
|
|
20
|
+
return el;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('useCitationRendering', () => {
|
|
24
|
+
describe('citationPlugins', () => {
|
|
25
|
+
it('adds no plugins when there are no citations', () => {
|
|
26
|
+
const { citationPlugins } = useCitationRendering(
|
|
27
|
+
() => undefined,
|
|
28
|
+
vi.fn(),
|
|
29
|
+
);
|
|
30
|
+
expect(citationPlugins.value).toEqual([]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('adds the remarkCitations plugin when citations are present', () => {
|
|
34
|
+
const { citationPlugins } = useCitationRendering(() => SOURCES, vi.fn());
|
|
35
|
+
expect(citationPlugins.value).toHaveLength(1);
|
|
36
|
+
const [plugin] = citationPlugins.value[0] as [unknown, unknown];
|
|
37
|
+
expect(plugin).toBe(remarkCitations);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('renderCitationNode', () => {
|
|
42
|
+
it('renders a matching placeholder as a CkCitation with the source props', () => {
|
|
43
|
+
const onOpenSource = vi.fn();
|
|
44
|
+
const { renderCitationNode } = useCitationRendering(
|
|
45
|
+
() => SOURCES,
|
|
46
|
+
onOpenSource,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const vnode = renderCitationNode(citationPlaceholder(1)) as VNode;
|
|
50
|
+
|
|
51
|
+
expect(vnode.type).toBe(CkCitation);
|
|
52
|
+
expect(vnode.props).toMatchObject({
|
|
53
|
+
index: 1,
|
|
54
|
+
fileName: 'q3-report.pdf',
|
|
55
|
+
url: '/api/v1/.../public-urls',
|
|
56
|
+
onOpenSource,
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('returns null for a placeholder with no matching source', () => {
|
|
61
|
+
const { renderCitationNode } = useCitationRendering(
|
|
62
|
+
() => SOURCES,
|
|
63
|
+
vi.fn(),
|
|
64
|
+
);
|
|
65
|
+
expect(renderCitationNode(citationPlaceholder(9))).toBeNull();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('returns null for a non-citation element', () => {
|
|
69
|
+
const { renderCitationNode } = useCitationRendering(
|
|
70
|
+
() => SOURCES,
|
|
71
|
+
vi.fn(),
|
|
72
|
+
);
|
|
73
|
+
expect(renderCitationNode(document.createElement('p'))).toBeNull();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('delegates to the slot when provided, passing source and openSource', () => {
|
|
77
|
+
const onOpenSource = vi.fn();
|
|
78
|
+
const slotResult = [h('span', 'custom')];
|
|
79
|
+
const slot = vi.fn(() => slotResult);
|
|
80
|
+
const { renderCitationNode } = useCitationRendering(
|
|
81
|
+
() => SOURCES,
|
|
82
|
+
onOpenSource,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const result = renderCitationNode(citationPlaceholder(1), slot);
|
|
86
|
+
|
|
87
|
+
expect(slot).toHaveBeenCalledWith({
|
|
88
|
+
source: SOURCES[0],
|
|
89
|
+
openSource: onOpenSource,
|
|
90
|
+
});
|
|
91
|
+
expect(result).toBe(slotResult);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { computed, h, toValue, type MaybeRefOrGetter, type VNode } from 'vue';
|
|
2
|
+
import type { PluggableList } from 'unified';
|
|
3
|
+
import { remarkCitations } from '../../../helpers/unified/remarkCitations';
|
|
4
|
+
import { CkCitation } from '../CkCitation';
|
|
5
|
+
import type { CkCitationSource } from '../CkCitation';
|
|
6
|
+
|
|
7
|
+
/** Scope passed to the `citation` slot when overriding how a source renders. */
|
|
8
|
+
export type CkCitationSlot = (props: {
|
|
9
|
+
source: CkCitationSource;
|
|
10
|
+
openSource: (url: string) => void;
|
|
11
|
+
}) => VNode[];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The citation concern of `CkMarkdownRenderer`, extracted so the renderer stays
|
|
15
|
+
* markdown-focused and this logic is testable on its own.
|
|
16
|
+
*/
|
|
17
|
+
export function useCitationRendering(
|
|
18
|
+
citations: MaybeRefOrGetter<CkCitationSource[] | undefined>,
|
|
19
|
+
onOpenSource: (url: string) => void,
|
|
20
|
+
) {
|
|
21
|
+
const sourcesByIndex = computed(
|
|
22
|
+
() =>
|
|
23
|
+
new Map(
|
|
24
|
+
(toValue(citations) ?? []).map((source) => [source.index, source]),
|
|
25
|
+
),
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const citationPlugins = computed<PluggableList>(() => {
|
|
29
|
+
const list = toValue(citations);
|
|
30
|
+
if (!list?.length) return [];
|
|
31
|
+
const indices = new Set(list.map((source) => source.index));
|
|
32
|
+
return [[remarkCitations, { indices }]];
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Returns null for non-citation elements so the renderer handles them normally.
|
|
36
|
+
function renderCitationNode(
|
|
37
|
+
el: Element,
|
|
38
|
+
slot?: CkCitationSlot,
|
|
39
|
+
): VNode | VNode[] | null {
|
|
40
|
+
if (!el.hasAttribute('data-citation-index')) return null;
|
|
41
|
+
|
|
42
|
+
const index = Number(el.getAttribute('data-citation-index'));
|
|
43
|
+
const source = sourcesByIndex.value.get(index);
|
|
44
|
+
if (!source) return null;
|
|
45
|
+
|
|
46
|
+
// Expose the same handler so slot content can forward `open-source`.
|
|
47
|
+
if (slot) return slot({ source, openSource: onOpenSource });
|
|
48
|
+
return h(CkCitation, { ...source, onOpenSource });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { citationPlugins, renderCitationNode };
|
|
52
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
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('
|
|
5
|
+
describe('CkMessageButton', () => {
|
|
6
6
|
it('renders slot content inside the button', () => {
|
|
7
|
-
render(
|
|
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(
|
|
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(
|
|
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(
|
|
34
|
+
render(CkMessageButton, {
|
|
35
35
|
props: { ariaLabel: 'Action' },
|
|
36
36
|
});
|
|
37
37
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
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
|
|
6
|
-
title: 'Base/
|
|
7
|
-
component:
|
|
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 {
|
|
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
|
-
<
|
|
37
|
+
<CkMessageButton ariaLabel="Copy message">
|
|
38
38
|
<UiIcon name="copy" :size="16" aria-hidden="true" />
|
|
39
|
-
</
|
|
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: {
|
|
48
|
+
components: { CkMessageButton, UiIcon },
|
|
49
49
|
template: `
|
|
50
|
-
<
|
|
50
|
+
<CkMessageButton ariaLabel="Copy message">
|
|
51
51
|
<UiIcon name="copy" :size="16" aria-hidden="true" />
|
|
52
|
-
</
|
|
52
|
+
</CkMessageButton>
|
|
53
53
|
`,
|
|
54
54
|
}),
|
|
55
55
|
parameters: { docs: { source: { code: defaultTemplateSource } } },
|