@aleph-alpha/chat-kit 5.7.0 → 5.9.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/{CkHistory.vue_vue_type_script_setup_true_lang-YHwE2DYx.js → CkHistory.vue_vue_type_script_setup_true_lang-CEqJtMuI.js} +81 -52
- package/dist/adapters/responses-api.d.ts.map +1 -1
- package/dist/adapters/responses-api.js +46 -33
- package/dist/chat-kit.css +1 -1
- package/dist/components/base/CkConversationLayout/CkConversationLayout.stories.d.ts +7 -0
- package/dist/components/base/CkConversationLayout/CkConversationLayout.stories.d.ts.map +1 -1
- package/dist/components/base/CkConversationLayout/CkConversationLayout.vue.d.ts +3 -1
- package/dist/components/base/CkConversationLayout/CkConversationLayout.vue.d.ts.map +1 -1
- package/dist/components/base/CkModelSelector/CkModelSelector.stories.d.ts +6 -0
- package/dist/components/base/CkModelSelector/CkModelSelector.stories.d.ts.map +1 -1
- package/dist/components/base/CkModelSelector/CkModelSelector.vue.d.ts.map +1 -1
- package/dist/components/base/CkModelSelector/types.d.ts +6 -0
- package/dist/components/base/CkModelSelector/types.d.ts.map +1 -1
- package/dist/components/composed/CkConversation/CkConversation.stories.d.ts +23 -3
- package/dist/components/composed/CkConversation/CkConversation.stories.d.ts.map +1 -1
- package/dist/components/composed/CkConversation/CkConversation.vue.d.ts +17 -2
- package/dist/components/composed/CkConversation/CkConversation.vue.d.ts.map +1 -1
- package/dist/components/composed/CkHistory/CkHistory.vue.d.ts +1 -1
- package/dist/components/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/adapters/responses-api.spec.ts +6 -3
- package/src/adapters/responses-api.ts +62 -35
- package/src/components/base/CkConversationLayout/CkConversationLayout.spec.ts +209 -1
- package/src/components/base/CkConversationLayout/CkConversationLayout.stories.ts +84 -0
- package/src/components/base/CkConversationLayout/CkConversationLayout.vue +59 -21
- package/src/components/base/CkModelSelector/CkModelSelector.spec.ts +23 -0
- package/src/components/base/CkModelSelector/CkModelSelector.stories.ts +20 -0
- package/src/components/base/CkModelSelector/CkModelSelector.vue +8 -1
- package/src/components/base/CkModelSelector/types.ts +6 -0
- package/src/components/composed/CkConversation/CkConversation.spec.ts +96 -5
- package/src/components/composed/CkConversation/CkConversation.stories.ts +328 -4
- package/src/components/composed/CkConversation/CkConversation.vue +38 -8
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
} from '../../../types';
|
|
10
10
|
import CkConversation from './CkConversation.vue';
|
|
11
11
|
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
|
12
|
-
import { ref } from 'vue';
|
|
12
|
+
import { h, ref, type VNode } from 'vue';
|
|
13
13
|
|
|
14
14
|
const now = new Date();
|
|
15
15
|
|
|
@@ -455,6 +455,17 @@ const customClasses = {
|
|
|
455
455
|
/>
|
|
456
456
|
</template>`;
|
|
457
457
|
|
|
458
|
+
const defaultCodeBlockTemplateSource = `<script setup lang="ts">
|
|
459
|
+
import { CkConversation } from '@aleph-alpha/chat-kit';
|
|
460
|
+
import type { BaseMessage } from '@aleph-alpha/chat-kit';
|
|
461
|
+
|
|
462
|
+
const messages: BaseMessage[] = [/* ... */];
|
|
463
|
+
</script>
|
|
464
|
+
|
|
465
|
+
<template>
|
|
466
|
+
<CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;" />
|
|
467
|
+
</template>`;
|
|
468
|
+
|
|
458
469
|
const withCustomCodeBlockTemplateSource = `<script setup lang="ts">
|
|
459
470
|
import { CkConversation } from '@aleph-alpha/chat-kit';
|
|
460
471
|
import type { BaseMessage } from '@aleph-alpha/chat-kit';
|
|
@@ -481,6 +492,60 @@ import { CkConversation } from '@aleph-alpha/chat-kit';
|
|
|
481
492
|
<CkConversation :messages="[]" style="height: 100%;" />
|
|
482
493
|
</template>`;
|
|
483
494
|
|
|
495
|
+
const scrollToMessageTemplateSource = `<script setup lang="ts">
|
|
496
|
+
import { CkConversation } from '@aleph-alpha/chat-kit';
|
|
497
|
+
import type { BaseMessage } from '@aleph-alpha/chat-kit';
|
|
498
|
+
import { ref, useTemplateRef } from 'vue';
|
|
499
|
+
|
|
500
|
+
const messages: BaseMessage[] = [/* ... */];
|
|
501
|
+
|
|
502
|
+
const conversation = useTemplateRef('conversation');
|
|
503
|
+
const index = ref(0);
|
|
504
|
+
|
|
505
|
+
function scrollToMessage(): void {
|
|
506
|
+
// Zero-based index among all rows; omit the argument to scroll to the last.
|
|
507
|
+
conversation.value?.scrollToMessage(index.value);
|
|
508
|
+
}
|
|
509
|
+
</script>
|
|
510
|
+
|
|
511
|
+
<template>
|
|
512
|
+
<div class="flex h-[400px] flex-col">
|
|
513
|
+
<div class="flex items-center gap-2 border-b p-2">
|
|
514
|
+
<label for="msg-index">Message index</label>
|
|
515
|
+
<input id="msg-index" v-model.number="index" type="number" min="0" />
|
|
516
|
+
<button @click="scrollToMessage">Scroll to message</button>
|
|
517
|
+
</div>
|
|
518
|
+
<CkConversation
|
|
519
|
+
ref="conversation"
|
|
520
|
+
:messages="messages"
|
|
521
|
+
agent-avatar-fallback="AI"
|
|
522
|
+
class="min-h-0 flex-1"
|
|
523
|
+
/>
|
|
524
|
+
</div>
|
|
525
|
+
<template>`;
|
|
526
|
+
|
|
527
|
+
const slotAnatomyTemplateSource = `<script setup lang="ts">
|
|
528
|
+
import { CkConversation } from '@aleph-alpha/chat-kit';
|
|
529
|
+
import type { BaseMessage } from '@aleph-alpha/chat-kit';
|
|
530
|
+
|
|
531
|
+
const messages: BaseMessage[] = [/* ... */];
|
|
532
|
+
</script>
|
|
533
|
+
|
|
534
|
+
<template>
|
|
535
|
+
<!--
|
|
536
|
+
Each slot has a default rendering. Overriding a parent slot replaces
|
|
537
|
+
everything nested beneath it, so the child slots below it no longer apply.
|
|
538
|
+
The anatomy story visualizes this "folding" relationship.
|
|
539
|
+
-->
|
|
540
|
+
<CkConversation :messages="messages">
|
|
541
|
+
<!-- Replaces the whole agent row: the header / content / footer slots
|
|
542
|
+
nested under it are no longer rendered. -->
|
|
543
|
+
<template #agent-message="{ message }">
|
|
544
|
+
<MyAgentBubble :message="message" />
|
|
545
|
+
</template>
|
|
546
|
+
</CkConversation>
|
|
547
|
+
</template>`;
|
|
548
|
+
|
|
484
549
|
/**
|
|
485
550
|
* Drop-in conversation UI. Pass `messages` with `role`, `content` (a list of
|
|
486
551
|
* text / tool / reasoning parts), and `status` fields and CkConversation
|
|
@@ -506,6 +571,167 @@ export const Default: Story = {
|
|
|
506
571
|
parameters: { docs: { source: { code: defaultTemplateSource } } },
|
|
507
572
|
};
|
|
508
573
|
|
|
574
|
+
/**
|
|
575
|
+
* A slot to which the consumer can pass content, describing what its default
|
|
576
|
+
* rendering is and which nested slots it supersedes when overridden.
|
|
577
|
+
*/
|
|
578
|
+
interface SlotDoc {
|
|
579
|
+
/** Slot name as used in `<template #name>`. */
|
|
580
|
+
name: string;
|
|
581
|
+
/** What overriding this slot replaces. */
|
|
582
|
+
summary: string;
|
|
583
|
+
/** Slots that live inside this one's default rendering. */
|
|
584
|
+
children?: SlotDoc[];
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* The slot hierarchy of `CkConversation`. Nesting mirrors the component's
|
|
589
|
+
* template: a child slot only renders as part of its parent's *default*
|
|
590
|
+
* rendering, so overriding a parent slot removes every child beneath it.
|
|
591
|
+
*/
|
|
592
|
+
const slotTree: SlotDoc[] = [
|
|
593
|
+
{
|
|
594
|
+
name: 'user-message',
|
|
595
|
+
summary: 'Replaces the entire user row.',
|
|
596
|
+
children: [
|
|
597
|
+
{
|
|
598
|
+
name: 'user-message-content',
|
|
599
|
+
summary: 'The content inside the default CkUserMessage bubble.',
|
|
600
|
+
},
|
|
601
|
+
],
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
name: 'agent-message',
|
|
605
|
+
summary: 'Replaces the entire agent row.',
|
|
606
|
+
children: [
|
|
607
|
+
{
|
|
608
|
+
name: 'agent-message-header',
|
|
609
|
+
summary: 'Replaces the whole CkAgentMessageHeader.',
|
|
610
|
+
children: [
|
|
611
|
+
{
|
|
612
|
+
name: 'agent-message-icon',
|
|
613
|
+
summary: 'The avatar shown for a completed message.',
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
name: 'agent-message-loading-icon',
|
|
617
|
+
summary: 'The avatar shown while the message is being generated.',
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
name: 'agent-message-header-content',
|
|
621
|
+
summary: 'The header text (e.g. a reasoning trail).',
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
name: 'agent-message-header-activity',
|
|
625
|
+
summary: 'An activity control on the right of the header.',
|
|
626
|
+
},
|
|
627
|
+
],
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
name: 'agent-message-content',
|
|
631
|
+
summary: 'Replaces the message body (the CkMarkdownRenderer).',
|
|
632
|
+
children: [
|
|
633
|
+
{
|
|
634
|
+
name: 'agent-message-code-block',
|
|
635
|
+
summary: 'Code blocks rendered inside the default markdown.',
|
|
636
|
+
},
|
|
637
|
+
],
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
name: 'agent-message-footer',
|
|
641
|
+
summary: 'Footer actions (a copy button by default).',
|
|
642
|
+
},
|
|
643
|
+
],
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
name: 'thread-footer',
|
|
647
|
+
summary: 'Trailing content appended after the last message.',
|
|
648
|
+
},
|
|
649
|
+
];
|
|
650
|
+
|
|
651
|
+
const slotPillStyle =
|
|
652
|
+
'font-family: monospace; font-size: 12px; background: #eef1fb; color: #33408a; ' +
|
|
653
|
+
'padding: 2px 6px; border-radius: 4px; white-space: nowrap;';
|
|
654
|
+
const slotSummaryTextStyle = 'color: #666; font-size: 13px;';
|
|
655
|
+
|
|
656
|
+
/** Recursively render a slot node as a foldable `<details>` tree. */
|
|
657
|
+
function renderSlotNode(node: SlotDoc): VNode {
|
|
658
|
+
const children = node.children ?? [];
|
|
659
|
+
const label = h(
|
|
660
|
+
'span',
|
|
661
|
+
{
|
|
662
|
+
style:
|
|
663
|
+
'display: inline-flex; align-items: baseline; gap: 8px; flex-wrap: wrap;',
|
|
664
|
+
},
|
|
665
|
+
[
|
|
666
|
+
h('code', { style: slotPillStyle }, node.name),
|
|
667
|
+
h('span', { style: slotSummaryTextStyle }, node.summary),
|
|
668
|
+
],
|
|
669
|
+
);
|
|
670
|
+
|
|
671
|
+
if (children.length === 0) {
|
|
672
|
+
return h('div', { style: 'padding: 6px 0 6px 20px;' }, [label]);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return h(
|
|
676
|
+
'details',
|
|
677
|
+
{
|
|
678
|
+
open: true,
|
|
679
|
+
style: 'padding: 6px 0;',
|
|
680
|
+
},
|
|
681
|
+
[
|
|
682
|
+
h('summary', { style: 'cursor: pointer; padding: 2px 0;' }, [label]),
|
|
683
|
+
h(
|
|
684
|
+
'div',
|
|
685
|
+
{
|
|
686
|
+
style:
|
|
687
|
+
'margin: 4px 0 0 8px; padding-left: 12px; border-left: 1px solid #e0e0e0;',
|
|
688
|
+
},
|
|
689
|
+
children.map(renderSlotNode),
|
|
690
|
+
),
|
|
691
|
+
],
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* A diagram of how CkConversation's slots nest. Every slot has a default
|
|
697
|
+
* rendering; overriding a parent slot replaces everything nested beneath it,
|
|
698
|
+
* so the child slots below it no longer apply. Collapse a node to see which
|
|
699
|
+
* slots "fold away" when you take over its parent.
|
|
700
|
+
*/
|
|
701
|
+
export const SlotAnatomy: Story = {
|
|
702
|
+
// Emoji prefix marks this out in the sidebar as a reference diagram rather
|
|
703
|
+
// than a rendered component example.
|
|
704
|
+
name: '🧩 Slot Anatomy',
|
|
705
|
+
render: () => ({
|
|
706
|
+
setup() {
|
|
707
|
+
return () =>
|
|
708
|
+
h(
|
|
709
|
+
'div',
|
|
710
|
+
{
|
|
711
|
+
// Fill and scroll within the meta decorator's fixed-height frame
|
|
712
|
+
// instead of being clipped by its `overflow: hidden`.
|
|
713
|
+
style:
|
|
714
|
+
'font-family: system-ui, sans-serif; padding: 16px; line-height: 1.5; ' +
|
|
715
|
+
'height: 100%; overflow-y: auto; box-sizing: border-box;',
|
|
716
|
+
},
|
|
717
|
+
[
|
|
718
|
+
h(
|
|
719
|
+
'p',
|
|
720
|
+
{ style: 'margin: 0 0 12px; color: #444; font-size: 13px;' },
|
|
721
|
+
'Every slot has a default rendering. Overriding a parent slot ' +
|
|
722
|
+
'replaces everything nested beneath it — the child slots below ' +
|
|
723
|
+
'it no longer apply. Collapse a node to see which slots fold away.',
|
|
724
|
+
),
|
|
725
|
+
...slotTree.map(renderSlotNode),
|
|
726
|
+
],
|
|
727
|
+
);
|
|
728
|
+
},
|
|
729
|
+
}),
|
|
730
|
+
parameters: {
|
|
731
|
+
docs: { source: { code: slotAnatomyTemplateSource } },
|
|
732
|
+
},
|
|
733
|
+
};
|
|
734
|
+
|
|
509
735
|
/**
|
|
510
736
|
* Uses a remote avatar image (with fallback text). Use when you want a
|
|
511
737
|
* branded agent identity in each reply.
|
|
@@ -826,9 +1052,61 @@ export const WithCustomMarkdownClasses: Story = {
|
|
|
826
1052
|
};
|
|
827
1053
|
|
|
828
1054
|
/**
|
|
829
|
-
*
|
|
830
|
-
*
|
|
831
|
-
*
|
|
1055
|
+
* Fenced code blocks in agent markdown render with `UiCodeBlock` out of the
|
|
1056
|
+
* box — syntax highlighting, a language label, and a copy button — without any
|
|
1057
|
+
* slot wiring.
|
|
1058
|
+
*/
|
|
1059
|
+
export const DefaultCodeBlock: Story = {
|
|
1060
|
+
render: () => ({
|
|
1061
|
+
components: { CkConversation },
|
|
1062
|
+
setup() {
|
|
1063
|
+
const messages: BaseMessage[] = [
|
|
1064
|
+
{
|
|
1065
|
+
id: '1',
|
|
1066
|
+
role: 'user',
|
|
1067
|
+
content: [text('Show me a code example')],
|
|
1068
|
+
createdAt: now,
|
|
1069
|
+
status: 'completed',
|
|
1070
|
+
},
|
|
1071
|
+
{
|
|
1072
|
+
id: '2',
|
|
1073
|
+
role: 'assistant',
|
|
1074
|
+
content: [
|
|
1075
|
+
text(
|
|
1076
|
+
[
|
|
1077
|
+
'Here is an example:',
|
|
1078
|
+
'',
|
|
1079
|
+
'```typescript',
|
|
1080
|
+
'const greeting = "Hello, world!";',
|
|
1081
|
+
'console.log(greeting);',
|
|
1082
|
+
'```',
|
|
1083
|
+
'',
|
|
1084
|
+
'And another one:',
|
|
1085
|
+
'',
|
|
1086
|
+
'```python',
|
|
1087
|
+
'def greet(name: str) -> str:',
|
|
1088
|
+
' return f"Hello, {name}!"',
|
|
1089
|
+
'```',
|
|
1090
|
+
].join('\n'),
|
|
1091
|
+
),
|
|
1092
|
+
],
|
|
1093
|
+
createdAt: now,
|
|
1094
|
+
status: 'completed',
|
|
1095
|
+
},
|
|
1096
|
+
];
|
|
1097
|
+
return { messages };
|
|
1098
|
+
},
|
|
1099
|
+
template: `
|
|
1100
|
+
<CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;" />
|
|
1101
|
+
`,
|
|
1102
|
+
}),
|
|
1103
|
+
parameters: { docs: { source: { code: defaultCodeBlockTemplateSource } } },
|
|
1104
|
+
};
|
|
1105
|
+
|
|
1106
|
+
/**
|
|
1107
|
+
* Fenced code blocks render with `UiCodeBlock` (syntax highlighting + copy
|
|
1108
|
+
* button) by default. Override that with a custom renderer (Prism, a bespoke
|
|
1109
|
+
* theme, etc.) via `#agent-message-code-block`, as shown here.
|
|
832
1110
|
*/
|
|
833
1111
|
export const WithCustomCodeBlock: Story = {
|
|
834
1112
|
render: () => ({
|
|
@@ -884,6 +1162,52 @@ export const WithCustomCodeBlock: Story = {
|
|
|
884
1162
|
parameters: { docs: { source: { code: withCustomCodeBlockTemplateSource } } },
|
|
885
1163
|
};
|
|
886
1164
|
|
|
1165
|
+
/**
|
|
1166
|
+
* Programmatic navigation via the exposed `scrollToMessage(index)` method. Enter
|
|
1167
|
+
* a zero-based row index and the conversation scrolls that message to the top.
|
|
1168
|
+
* Index counts all rows regardless of role (a `thread-footer`, when present, is
|
|
1169
|
+
* the trailing row); omitting the index scrolls to the last row.
|
|
1170
|
+
*/
|
|
1171
|
+
export const ScrollToMessage: Story = {
|
|
1172
|
+
render: () => ({
|
|
1173
|
+
components: { CkConversation },
|
|
1174
|
+
setup() {
|
|
1175
|
+
const messages: BaseMessage[] = Array.from({ length: 10 }, (_, i) => ({
|
|
1176
|
+
id: `m-${i}`,
|
|
1177
|
+
role: i % 2 === 0 ? 'user' : 'assistant',
|
|
1178
|
+
content: [
|
|
1179
|
+
text(
|
|
1180
|
+
`[${i}] ${
|
|
1181
|
+
i % 2 === 0 ? 'User' : 'Agent'
|
|
1182
|
+
} message number ${i}. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore.`,
|
|
1183
|
+
),
|
|
1184
|
+
],
|
|
1185
|
+
createdAt: now,
|
|
1186
|
+
status: 'completed',
|
|
1187
|
+
}));
|
|
1188
|
+
const conversation = ref();
|
|
1189
|
+
const index = ref(0);
|
|
1190
|
+
function scrollToMessage() {
|
|
1191
|
+
conversation.value?.scrollToMessage(index.value);
|
|
1192
|
+
}
|
|
1193
|
+
return { messages, conversation, index, scrollToMessage };
|
|
1194
|
+
},
|
|
1195
|
+
template: `
|
|
1196
|
+
<div style="display: flex; flex-direction: column; height: 100%;">
|
|
1197
|
+
<div style="display: flex; align-items: center; gap: 8px; padding: 8px; border-bottom: 1px solid #e0e0e0;">
|
|
1198
|
+
<label for="msg-index">Message index</label>
|
|
1199
|
+
<input id="msg-index" v-model.number="index" type="number" min="0" style="width: 72px; padding: 4px 8px; border: 1px solid #ccc; border-radius: 6px;" />
|
|
1200
|
+
<button @click="scrollToMessage" style="padding: 6px 16px; border-radius: 6px; background: #333; color: white; cursor: pointer;">
|
|
1201
|
+
Scroll to message
|
|
1202
|
+
</button>
|
|
1203
|
+
</div>
|
|
1204
|
+
<CkConversation ref="conversation" :messages="messages" agent-avatar-fallback="AI" style="flex: 1; min-height: 0;" />
|
|
1205
|
+
</div>
|
|
1206
|
+
`,
|
|
1207
|
+
}),
|
|
1208
|
+
parameters: { docs: { source: { code: scrollToMessageTemplateSource } } },
|
|
1209
|
+
};
|
|
1210
|
+
|
|
887
1211
|
/**
|
|
888
1212
|
* Empty state — an initial view before the first turn. Use to show a welcome
|
|
889
1213
|
* screen, quick prompts, or onboarding content above this surface.
|
|
@@ -12,9 +12,17 @@ import {
|
|
|
12
12
|
UiAvatar,
|
|
13
13
|
UiAvatarFallback,
|
|
14
14
|
UiAvatarImage,
|
|
15
|
+
UiCodeBlock,
|
|
15
16
|
UiSpinner,
|
|
16
17
|
} from '@aleph-alpha/ui-library';
|
|
17
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
Comment,
|
|
20
|
+
Fragment,
|
|
21
|
+
Text,
|
|
22
|
+
computed,
|
|
23
|
+
useTemplateRef,
|
|
24
|
+
type VNode,
|
|
25
|
+
} from 'vue';
|
|
18
26
|
|
|
19
27
|
defineOptions({ name: 'CkConversation' });
|
|
20
28
|
|
|
@@ -53,7 +61,11 @@ const slots = defineSlots<{
|
|
|
53
61
|
'agent-message-content': (props: { message: BaseMessage }) => void;
|
|
54
62
|
/** Provide footer actions for the agent message */
|
|
55
63
|
'agent-message-footer': (props: { message: BaseMessage }) => void;
|
|
56
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* Replace the default code block rendering inside agent markdown. When not
|
|
66
|
+
* provided, fenced code blocks render with `UiCodeBlock` (syntax highlighting
|
|
67
|
+
* + copy button).
|
|
68
|
+
*/
|
|
57
69
|
'agent-message-code-block': (props: {
|
|
58
70
|
code: string;
|
|
59
71
|
language: string;
|
|
@@ -122,10 +134,26 @@ function hasHeaderActivity(message: BaseMessage): boolean {
|
|
|
122
134
|
| undefined;
|
|
123
135
|
return slot ? slot({ message }).some((vnode) => !isVNodeEmpty(vnode)) : false;
|
|
124
136
|
}
|
|
137
|
+
|
|
138
|
+
const layoutRef = useTemplateRef('layoutRef');
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Scroll so the message at `index` sits at the top of the viewport. `index` is
|
|
142
|
+
* the zero-based position among the rendered rows (any `thread-footer` counts
|
|
143
|
+
* as the trailing row); when omitted it scrolls to the last row.
|
|
144
|
+
*
|
|
145
|
+
* This only performs the scroll — it does not focus the message. Any
|
|
146
|
+
* accompanying focus handling must be done separately by the caller.
|
|
147
|
+
*/
|
|
148
|
+
function scrollToMessage(index?: number): void {
|
|
149
|
+
layoutRef.value?.scrollToMessage(index);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
defineExpose({ scrollToMessage });
|
|
125
153
|
</script>
|
|
126
154
|
|
|
127
155
|
<template>
|
|
128
|
-
<CkConversationLayout :messages="messages">
|
|
156
|
+
<CkConversationLayout ref="layoutRef" :messages="messages">
|
|
129
157
|
<template #user-message="{ message }">
|
|
130
158
|
<div :class="{ 'ml-8': hasIcon }">
|
|
131
159
|
<slot name="user-message" :message="message">
|
|
@@ -213,11 +241,13 @@ function hasHeaderActivity(message: BaseMessage): boolean {
|
|
|
213
241
|
:content="getMessageText(message)"
|
|
214
242
|
:classes="agentMarkdownClasses"
|
|
215
243
|
>
|
|
216
|
-
<template
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
244
|
+
<template #code-block="codeBlockProps">
|
|
245
|
+
<slot name="agent-message-code-block" v-bind="codeBlockProps">
|
|
246
|
+
<UiCodeBlock
|
|
247
|
+
:code="codeBlockProps.code"
|
|
248
|
+
:language="codeBlockProps.language || 'text'"
|
|
249
|
+
/>
|
|
250
|
+
</slot>
|
|
221
251
|
</template>
|
|
222
252
|
</CkMarkdownRenderer>
|
|
223
253
|
</slot>
|