@aleph-alpha/chat-kit 5.0.1 → 5.1.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.
@@ -1,4 +1,4 @@
1
- import { defineComponent, openBlock, createBlock, unref, createElementBlock, createElementVNode, normalizeClass, renderSlot, createCommentVNode, mergeProps, withCtx, computed, createVNode, toDisplayString, ref, onBeforeUnmount, onMounted, nextTick, onUnmounted, watch, Fragment, renderList, normalizeStyle, Transition, createStaticVNode, Teleport, withModifiers, toValue, h, toRefs, useId, createTextVNode, useAttrs, useSlots, resolveDynamicComponent, useTemplateRef, onUpdated, createSlots, normalizeProps, guardReactiveProps } from "vue";
1
+ import { defineComponent, openBlock, createBlock, unref, createElementBlock, createElementVNode, normalizeClass, renderSlot, createCommentVNode, mergeProps, withCtx, computed, createVNode, toDisplayString, ref, onBeforeUnmount, onMounted, nextTick, onUnmounted, watch, Fragment, renderList, normalizeStyle, Transition, createStaticVNode, Teleport, withModifiers, toValue, h, toRefs, useId, createTextVNode, useAttrs, useSlots, resolveDynamicComponent, useTemplateRef, onUpdated, createSlots, normalizeProps, guardReactiveProps, Comment, Text } from "vue";
2
2
  import { UiIcon, UiButton, UiFileType, UiPopover, UiPopoverTrigger, UiPopoverContent, UiTooltip, formatFileSize, UiDialog, UiDialogContent, UiDialogDescription, UiFieldLabel, UiToggleGroup, UiToggleGroupItem, UiField, UiTextarea, UiFileItem, UiProgress, Primitive, UiAvatar, UiAvatarImage, UiSpinner, UiAvatarFallback, UiSidebarPopoverMenu, UiSidebarPopoverMenuItem } from "@aleph-alpha/ui-library";
3
3
  import { u as useChatKitLabels, d as defaultChatKitLabels, r as remarkCitations, a as useMarkdownParser } from "./useChatKitLabels-zMFV7aEz.js";
4
4
  const MINUTE = 60;
@@ -45,7 +45,7 @@ const _sfc_main$s = /* @__PURE__ */ defineComponent({
45
45
  };
46
46
  }
47
47
  });
48
- const _hoisted_1$g = {
48
+ const _hoisted_1$h = {
49
49
  xmlns: "http://www.w3.org/2000/svg",
50
50
  width: "16",
51
51
  height: "16",
@@ -57,7 +57,7 @@ const _sfc_main$r = /* @__PURE__ */ defineComponent({
57
57
  __name: "CkStopIcon",
58
58
  setup(__props) {
59
59
  return (_ctx, _cache) => {
60
- return openBlock(), createElementBlock("svg", _hoisted_1$g, [..._cache[0] || (_cache[0] = [
60
+ return openBlock(), createElementBlock("svg", _hoisted_1$h, [..._cache[0] || (_cache[0] = [
61
61
  createElementVNode("path", {
62
62
  d: "M13.3335 3.33301C13.3335 2.96482 13.035 2.66634 12.6668 2.66634H3.3335C2.96531 2.66634 2.66683 2.96482 2.66683 3.33301V12.6663C2.66683 13.0345 2.96531 13.333 3.3335 13.333H12.6668C13.035 13.333 13.3335 13.0345 13.3335 12.6663V3.33301ZM14.6668 12.6663C14.6668 13.7709 13.7714 14.6663 12.6668 14.6663H3.3335C2.22893 14.6663 1.3335 13.7709 1.3335 12.6663V3.33301C1.3335 2.22844 2.22893 1.33301 3.3335 1.33301H12.6668C13.7714 1.33301 14.6668 2.22844 14.6668 3.33301V12.6663Z",
63
63
  fill: "currentColor"
@@ -66,7 +66,7 @@ const _sfc_main$r = /* @__PURE__ */ defineComponent({
66
66
  };
67
67
  }
68
68
  });
69
- const _hoisted_1$f = {
69
+ const _hoisted_1$g = {
70
70
  key: 0,
71
71
  "data-testid": "ck-agent-message-header",
72
72
  class: "w-full min-w-0"
@@ -90,7 +90,7 @@ const _sfc_main$q = /* @__PURE__ */ defineComponent({
90
90
  __props.bottomSeparator && "border-border-surface-default border-b"
91
91
  ])
92
92
  }, [
93
- _ctx.$slots.header ? (openBlock(), createElementBlock("div", _hoisted_1$f, [
93
+ _ctx.$slots.header ? (openBlock(), createElementBlock("div", _hoisted_1$g, [
94
94
  renderSlot(_ctx.$slots, "header")
95
95
  ])) : createCommentVNode("", true),
96
96
  createElementVNode("div", _hoisted_2$a, [
@@ -103,7 +103,7 @@ const _sfc_main$q = /* @__PURE__ */ defineComponent({
103
103
  };
104
104
  }
105
105
  });
106
- const _hoisted_1$e = {
106
+ const _hoisted_1$f = {
107
107
  key: 0,
108
108
  "data-testid": "ck-agent-message-header-icon",
109
109
  class: "flex flex-shrink-0 items-center justify-center"
@@ -126,7 +126,7 @@ const _sfc_main$p = /* @__PURE__ */ defineComponent({
126
126
  _ctx.$slots.activity && "border-border-surface-default border-b py-4"
127
127
  ])
128
128
  }, [
129
- _ctx.$slots.icon ? (openBlock(), createElementBlock("div", _hoisted_1$e, [
129
+ _ctx.$slots.icon ? (openBlock(), createElementBlock("div", _hoisted_1$f, [
130
130
  renderSlot(_ctx.$slots, "icon")
131
131
  ])) : createCommentVNode("", true),
132
132
  createElementVNode("span", _hoisted_2$9, [
@@ -181,7 +181,7 @@ const _sfc_main$o = /* @__PURE__ */ defineComponent({
181
181
  };
182
182
  }
183
183
  });
184
- const _hoisted_1$d = { class: "ck-citation-card flex w-[22.5rem] max-w-[90vw] flex-col gap-2 rounded-md bg-background-surface-floating px-6 pb-6 pt-4 shadow-md" };
184
+ const _hoisted_1$e = { class: "ck-citation-card flex w-[22.5rem] max-w-[90vw] flex-col gap-2 rounded-md bg-background-surface-floating px-6 pb-6 pt-4 shadow-md" };
185
185
  const _hoisted_2$8 = { class: "flex items-center gap-2" };
186
186
  const _hoisted_3$7 = ["title"];
187
187
  const _hoisted_4$6 = { class: "line-clamp-6 break-words text-sm leading-5 text-content-on-surface-secondary" };
@@ -215,7 +215,7 @@ const _sfc_main$n = /* @__PURE__ */ defineComponent({
215
215
  }
216
216
  }
217
217
  return (_ctx, _cache) => {
218
- return openBlock(), createElementBlock("div", _hoisted_1$d, [
218
+ return openBlock(), createElementBlock("div", _hoisted_1$e, [
219
219
  createElementVNode("div", _hoisted_2$8, [
220
220
  createVNode(unref(UiFileType), {
221
221
  filename: __props.fileName,
@@ -249,7 +249,7 @@ const _sfc_main$n = /* @__PURE__ */ defineComponent({
249
249
  };
250
250
  }
251
251
  });
252
- const _hoisted_1$c = ["aria-label"];
252
+ const _hoisted_1$d = ["aria-label"];
253
253
  const CLOSE_DELAY_MS = 80;
254
254
  const _sfc_main$m = /* @__PURE__ */ defineComponent({
255
255
  ...{
@@ -337,7 +337,7 @@ const _sfc_main$m = /* @__PURE__ */ defineComponent({
337
337
  onKeydown: onTriggerKeydown,
338
338
  onMouseenter: onPointerEnter,
339
339
  onMouseleave: onPointerLeave
340
- }, toDisplayString(__props.index), 43, _hoisted_1$c)
340
+ }, toDisplayString(__props.index), 43, _hoisted_1$d)
341
341
  ]),
342
342
  _: 1
343
343
  }),
@@ -378,7 +378,7 @@ const _export_sfc = (sfc, props) => {
378
378
  return target;
379
379
  };
380
380
  const CkCitation = /* @__PURE__ */ _export_sfc(_sfc_main$m, [["__scopeId", "data-v-73025da4"]]);
381
- const _hoisted_1$b = ["data-message-row"];
381
+ const _hoisted_1$c = ["data-message-row"];
382
382
  const _hoisted_2$7 = {
383
383
  key: 0,
384
384
  class: "flex min-w-0 justify-end"
@@ -533,7 +533,7 @@ const _sfc_main$l = /* @__PURE__ */ defineComponent({
533
533
  ])) : (openBlock(), createElementBlock("div", _hoisted_3$6, [
534
534
  renderSlot(_ctx.$slots, "agent-message", { message })
535
535
  ]))
536
- ], 8, _hoisted_1$b);
536
+ ], 8, _hoisted_1$c);
537
537
  }), 128)),
538
538
  hasBottomSpacer.value ? (openBlock(), createElementBlock("div", {
539
539
  key: 0,
@@ -568,7 +568,7 @@ const _sfc_main$k = /* @__PURE__ */ defineComponent({
568
568
  };
569
569
  }
570
570
  });
571
- const _hoisted_1$a = {
571
+ const _hoisted_1$b = {
572
572
  class: "sr-only",
573
573
  "aria-live": "polite"
574
574
  };
@@ -656,7 +656,7 @@ const _sfc_main$j = /* @__PURE__ */ defineComponent({
656
656
  ]),
657
657
  _: 1
658
658
  }, 16, ["disabled"]),
659
- createElementVNode("span", _hoisted_1$a, toDisplayString(copied.value ? __props.ariaLabelCopied : __props.ariaLabelCopy), 1)
659
+ createElementVNode("span", _hoisted_1$b, toDisplayString(copied.value ? __props.ariaLabelCopied : __props.ariaLabelCopy), 1)
660
660
  ]),
661
661
  _: 1
662
662
  }, 8, ["open", "content"]);
@@ -664,7 +664,7 @@ const _sfc_main$j = /* @__PURE__ */ defineComponent({
664
664
  }
665
665
  });
666
666
  const CkCopyMessageButton = /* @__PURE__ */ _export_sfc(_sfc_main$j, [["__scopeId", "data-v-ba2e5f32"]]);
667
- const _hoisted_1$9 = {
667
+ const _hoisted_1$a = {
668
668
  width: "208",
669
669
  height: "144",
670
670
  viewBox: "0 0 208 144",
@@ -678,13 +678,13 @@ const _sfc_main$i = /* @__PURE__ */ defineComponent({
678
678
  __name: "CkDropZoneIllustration",
679
679
  setup(__props) {
680
680
  return (_ctx, _cache) => {
681
- return openBlock(), createElementBlock("svg", _hoisted_1$9, [..._cache[0] || (_cache[0] = [
681
+ return openBlock(), createElementBlock("svg", _hoisted_1$a, [..._cache[0] || (_cache[0] = [
682
682
  createStaticVNode('<g clip-path="url(#ck-drop-zone-clip)"><path d="M0 71.5391C0 32.0292 32.0292 0 71.5391 0H135.739C175.249 0 207.278 32.0292 207.278 71.5391C207.278 111.049 175.249 143.078 135.739 143.078H71.5392C32.0292 143.078 0 111.049 0 71.5391Z" fill="#F2EBE8"></path><path d="M120.899 98.2608L134.973 45.7385C135.223 44.805 136.182 44.251 137.116 44.5011L172.976 54.1098C173.909 54.3599 174.463 55.3195 174.213 56.2531L160.14 108.775C159.89 109.709 158.93 110.263 157.997 110.013L122.137 100.404C121.203 100.154 120.649 99.1943 120.899 98.2608Z" fill="#FFCCBB" stroke="#333333"></path><g><path d="M153.839 69.86L144.18 67.2718C143.118 66.9871 142.015 67.6235 141.731 68.686L137.589 84.1409C137.305 85.2034 137.941 86.3056 139.004 86.5904L150.595 89.6962C151.657 89.9809 152.76 89.3445 153.044 88.282L156.668 74.759L153.839 69.86Z" stroke="#1D1D1B" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"></path><path d="M153.839 69.86L153.063 72.7578C152.921 73.2891 153.239 73.8402 153.77 73.9825L156.668 74.759" stroke="#1D1D1B" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"></path><path d="M143.783 76.4828L151.51 78.5534" stroke="#1D1D1B" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"></path><path d="M143.006 79.3806L150.733 81.4511" stroke="#1D1D1B" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"></path><path d="M142.23 82.2784L147.059 83.5725" stroke="#1D1D1B" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"></path></g><path d="M86.3795 98.2608L72.3062 45.7385C72.0561 44.805 71.0965 44.251 70.1629 44.5011L34.3029 54.1098C33.3694 54.3599 32.8153 55.3195 33.0655 56.2531L47.1388 108.775C47.3889 109.709 48.3485 110.263 49.2821 110.013L85.1421 100.404C86.0756 100.154 86.6297 99.1943 86.3795 98.2608Z" fill="#FFE9B5" stroke="#333333"></path><g><path d="M60.3294 76.0227L58.0001 67.3293C60.3057 66.7116 62.7623 67.035 64.8294 68.2284C66.8966 69.4219 68.405 71.3877 69.0228 73.6933L60.3294 76.0227Z" stroke="#1D1D1B" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"></path><path d="M55.3303 68.8899C54.4077 69.3934 53.5755 70.0573 52.8752 70.8559C51.7015 72.1942 50.9511 73.8504 50.7187 75.6152C50.4864 77.38 50.7826 79.1741 51.5699 80.7706C52.3572 82.367 53.6002 83.6942 55.1417 84.5842C56.6833 85.4742 58.4541 85.8871 60.2304 85.7707C62.0066 85.6543 63.7084 85.0138 65.1206 83.9302C66.5328 82.8465 67.5919 81.3685 68.1641 79.6829C68.5135 78.6536 68.6717 77.5751 68.6371 76.4998" stroke="#1D1D1B" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"></path></g><path d="M76.5186 107.5L76.5185 35C76.5185 33.6193 77.6378 32.5 79.0185 32.5H128.519C129.899 32.5 131.019 33.6193 131.019 35V107.5C131.019 108.881 129.899 110 128.519 110H79.0186C77.6378 110 76.5186 108.881 76.5186 107.5Z" fill="#FFB3DD" stroke="#333333"></path><g><path d="M107.519 56.8335H95.519C94.419 56.8335 93.519 57.7335 93.519 58.8335V82.1665C93.519 83.2675 94.419 84.1665 95.519 84.1665H111.519C112.619 84.1665 113.519 83.2675 113.519 82.1665V64.8335L107.519 56.8335Z" stroke="#1D1D1B" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"></path><path d="M107.519 56.8335V62.8335C107.519 63.9335 108.419 64.8335 109.519 64.8335H113.519" stroke="#1D1D1B" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"></path><path d="M103.519 78.8335V70.8335" stroke="#1D1D1B" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"></path><path d="M99.519 74.8335L103.519 70.8335L107.519 74.8335" stroke="#1D1D1B" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"></path></g></g><defs><clipPath id="ck-drop-zone-clip"><rect width="208" height="144" fill="white"></rect></clipPath></defs>', 2)
683
683
  ])]);
684
684
  };
685
685
  }
686
686
  });
687
- const _hoisted_1$8 = ["aria-label"];
687
+ const _hoisted_1$9 = ["aria-label"];
688
688
  const _hoisted_2$6 = { class: "pointer-events-none flex h-full w-full flex-col items-center justify-center gap-4 text-content-on-surface-primary" };
689
689
  const _hoisted_3$5 = { class: "flex flex-col items-center gap-1 text-center" };
690
690
  const _hoisted_4$5 = { class: "body-16-bold" };
@@ -868,7 +868,7 @@ const _sfc_main$h = /* @__PURE__ */ defineComponent({
868
868
  ])
869
869
  ], true)
870
870
  ])
871
- ], 40, _hoisted_1$8)) : createCommentVNode("", true)
871
+ ], 40, _hoisted_1$9)) : createCommentVNode("", true)
872
872
  ]),
873
873
  _: 3
874
874
  }),
@@ -887,7 +887,7 @@ const _sfc_main$h = /* @__PURE__ */ defineComponent({
887
887
  }
888
888
  });
889
889
  const CkDropZoneOverlay = /* @__PURE__ */ _export_sfc(_sfc_main$h, [["__scopeId", "data-v-54119668"]]);
890
- const _hoisted_1$7 = ["aria-label"];
890
+ const _hoisted_1$8 = ["aria-label"];
891
891
  const _sfc_main$g = /* @__PURE__ */ defineComponent({
892
892
  ...{
893
893
  name: "CkFeedbackButton"
@@ -996,7 +996,7 @@ const _sfc_main$g = /* @__PURE__ */ defineComponent({
996
996
  ]),
997
997
  _: 1
998
998
  }, 8, ["content"])
999
- ], 8, _hoisted_1$7);
999
+ ], 8, _hoisted_1$8);
1000
1000
  };
1001
1001
  }
1002
1002
  });
@@ -1141,7 +1141,7 @@ const _sfc_main$f = defineComponent({
1141
1141
  };
1142
1142
  }
1143
1143
  });
1144
- const _hoisted_1$6 = {
1144
+ const _hoisted_1$7 = {
1145
1145
  key: 0,
1146
1146
  class: "flex min-h-0 flex-col gap-2"
1147
1147
  };
@@ -1304,7 +1304,7 @@ const _sfc_main$e = /* @__PURE__ */ defineComponent({
1304
1304
  showResponseColumn.value ? "grid-cols-2" : "grid-cols-1"
1305
1305
  ])
1306
1306
  }, [
1307
- showResponseColumn.value ? (openBlock(), createElementBlock("section", _hoisted_1$6, [
1307
+ showResponseColumn.value ? (openBlock(), createElementBlock("section", _hoisted_1$7, [
1308
1308
  createVNode(unref(UiFieldLabel), null, {
1309
1309
  default: withCtx(() => [
1310
1310
  createTextVNode(toDisplayString(responseHeading.value), 1)
@@ -1422,7 +1422,7 @@ const _sfc_main$d = /* @__PURE__ */ defineComponent({
1422
1422
  };
1423
1423
  }
1424
1424
  });
1425
- const _hoisted_1$5 = ["aria-label"];
1425
+ const _hoisted_1$6 = ["aria-label"];
1426
1426
  const _hoisted_2$4 = ["onClick"];
1427
1427
  const _hoisted_3$3 = ["disabled", "aria-label"];
1428
1428
  const _hoisted_4$3 = {
@@ -1577,7 +1577,7 @@ const _sfc_main$c = /* @__PURE__ */ defineComponent({
1577
1577
  }, 8, ["file", "size", "is-removable", "remove-label", "disabled", "onRemove"])
1578
1578
  ], 12, _hoisted_2$4);
1579
1579
  }), 128))
1580
- ], 8, _hoisted_1$5)) : (openBlock(), createBlock(unref(UiPopover), {
1580
+ ], 8, _hoisted_1$6)) : (openBlock(), createBlock(unref(UiPopover), {
1581
1581
  key: 1,
1582
1582
  open: isOpen.value,
1583
1583
  "onUpdate:open": _cache[0] || (_cache[0] = ($event) => isOpen.value = $event)
@@ -1669,7 +1669,7 @@ const _sfc_main$c = /* @__PURE__ */ defineComponent({
1669
1669
  }
1670
1670
  });
1671
1671
  const CHAT_HISTORY_ACTIVE_INDICATOR_CLASS = "ui-active-indicator";
1672
- const _hoisted_1$4 = ["data-active"];
1672
+ const _hoisted_1$5 = ["data-active"];
1673
1673
  const _hoisted_2$3 = { class: "min-w-0 flex-1" };
1674
1674
  const _hoisted_3$2 = { class: "truncate body-14-regular text-content-on-surface-primary" };
1675
1675
  const _hoisted_4$2 = {
@@ -1927,7 +1927,7 @@ const _sfc_main$b = /* @__PURE__ */ defineComponent({
1927
1927
  }))
1928
1928
  ], 42, _hoisted_10))
1929
1929
  ], 34)) : createCommentVNode("", true)
1930
- ], 10, _hoisted_1$4);
1930
+ ], 10, _hoisted_1$5);
1931
1931
  };
1932
1932
  }
1933
1933
  });
@@ -1962,7 +1962,7 @@ const _sfc_main$a = /* @__PURE__ */ defineComponent({
1962
1962
  };
1963
1963
  }
1964
1964
  });
1965
- const _hoisted_1$3 = ["disabled", "aria-expanded", "aria-label"];
1965
+ const _hoisted_1$4 = ["disabled", "aria-expanded", "aria-label"];
1966
1966
  const _hoisted_2$2 = { class: "min-w-0 max-w-[10rem] truncate" };
1967
1967
  const _hoisted_3$1 = ["aria-label"];
1968
1968
  const _hoisted_4$1 = ["aria-selected", "onClick"];
@@ -2024,7 +2024,7 @@ const _sfc_main$9 = /* @__PURE__ */ defineComponent({
2024
2024
  class: "shrink-0",
2025
2025
  "aria-hidden": "true"
2026
2026
  })
2027
- ], 8, _hoisted_1$3)
2027
+ ], 8, _hoisted_1$4)
2028
2028
  ]),
2029
2029
  _: 1
2030
2030
  }),
@@ -2157,7 +2157,7 @@ const _sfc_main$6 = /* @__PURE__ */ defineComponent({
2157
2157
  };
2158
2158
  }
2159
2159
  });
2160
- const _hoisted_1$2 = { class: "ck-prompt-root flex flex-col gap-2" };
2160
+ const _hoisted_1$3 = { class: "ck-prompt-root flex flex-col gap-2" };
2161
2161
  const _hoisted_2$1 = {
2162
2162
  key: 0,
2163
2163
  style: { "grid-area": "top" }
@@ -2254,7 +2254,7 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({
2254
2254
  onMounted(adjustHeight);
2255
2255
  __expose({ focus, submit });
2256
2256
  return (_ctx, _cache) => {
2257
- return openBlock(), createElementBlock("div", _hoisted_1$2, [
2257
+ return openBlock(), createElementBlock("div", _hoisted_1$3, [
2258
2258
  createElementVNode("form", {
2259
2259
  class: normalizeClass([
2260
2260
  "border-border-surface-default bg-background-input-strong min-h-17 grid items-center gap-x-0 gap-y-5 border p-4 transition-[border-radius] duration-250 ease-[cubic-bezier(0.22,1,0.36,1)] ",
@@ -2317,7 +2317,7 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({
2317
2317
  }
2318
2318
  });
2319
2319
  const CkPromptInput = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["__scopeId", "data-v-d828da46"]]);
2320
- const _hoisted_1$1 = {
2320
+ const _hoisted_1$2 = {
2321
2321
  key: 0,
2322
2322
  class: "text-center text-content-on-surface-muted text-xs font-normal leading-4"
2323
2323
  };
@@ -2329,7 +2329,7 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({
2329
2329
  },
2330
2330
  setup(__props) {
2331
2331
  return (_ctx, _cache) => {
2332
- return _ctx.$slots.default || __props.text ? (openBlock(), createElementBlock("p", _hoisted_1$1, [
2332
+ return _ctx.$slots.default || __props.text ? (openBlock(), createElementBlock("p", _hoisted_1$2, [
2333
2333
  renderSlot(_ctx.$slots, "default", {}, () => [
2334
2334
  createTextVNode(toDisplayString(__props.text), 1)
2335
2335
  ])
@@ -2383,7 +2383,7 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({
2383
2383
  };
2384
2384
  }
2385
2385
  });
2386
- const _hoisted_1 = { class: "inline-flex max-w-[536px] flex-col items-end justify-start gap-1" };
2386
+ const _hoisted_1$1 = { class: "inline-flex max-w-[536px] flex-col items-end justify-start gap-1" };
2387
2387
  const _hoisted_2 = ["aria-expanded"];
2388
2388
  const _sfc_main$2 = /* @__PURE__ */ defineComponent({
2389
2389
  ...{ name: "CkUserMessage" },
@@ -2427,7 +2427,7 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
2427
2427
  nextTick(measure);
2428
2428
  });
2429
2429
  return (_ctx, _cache) => {
2430
- return openBlock(), createElementBlock("div", _hoisted_1, [
2430
+ return openBlock(), createElementBlock("div", _hoisted_1$1, [
2431
2431
  createElementVNode("div", {
2432
2432
  ref_key: "contentRef",
2433
2433
  ref: contentRef,
@@ -2455,6 +2455,10 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
2455
2455
  }
2456
2456
  });
2457
2457
  const CkUserMessage = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-c759a863"]]);
2458
+ const _hoisted_1 = {
2459
+ key: 1,
2460
+ class: "flex size-8 shrink-0 items-center justify-center rounded-2xl bg-background-surface-muted-subtle"
2461
+ };
2458
2462
  const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2459
2463
  ...{ name: "CkConversation" },
2460
2464
  __name: "CkConversation",
@@ -2482,6 +2486,19 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2482
2486
  function getMessageText(message) {
2483
2487
  return message.content.filter((c) => c.type === "text").map((c) => c.text).join("");
2484
2488
  }
2489
+ function isVNodeEmpty(vnode) {
2490
+ if (vnode.type === Comment) return true;
2491
+ if (vnode.type === Text) return String(vnode.children ?? "").trim() === "";
2492
+ if (vnode.type === Fragment) {
2493
+ const children = vnode.children;
2494
+ return !children || children.every(isVNodeEmpty);
2495
+ }
2496
+ return false;
2497
+ }
2498
+ function hasHeaderActivity(message) {
2499
+ const slot = slots["agent-message-header-activity"];
2500
+ return slot ? slot({ message }).some((vnode) => !isVNodeEmpty(vnode)) : false;
2501
+ }
2485
2502
  return (_ctx, _cache) => {
2486
2503
  return openBlock(), createBlock(_sfc_main$l, { messages: __props.messages }, {
2487
2504
  "user-message": withCtx(({ message }) => [
@@ -2533,7 +2550,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2533
2550
  ]),
2534
2551
  _: 2
2535
2552
  }, [
2536
- hasIcon.value || _ctx.$slots["agent-message-header"] || _ctx.$slots["agent-message-header-content"] || _ctx.$slots["agent-message-header-activity"] ? {
2553
+ hasIcon.value || _ctx.$slots["agent-message-header"] || _ctx.$slots["agent-message-header-content"] || hasHeaderActivity(message) ? {
2537
2554
  name: "header",
2538
2555
  fn: withCtx(() => [
2539
2556
  renderSlot(_ctx.$slots, "agent-message-header", { message }, () => [
@@ -2552,7 +2569,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2552
2569
  }, () => [
2553
2570
  __props.agentLoadingImage ? (openBlock(), createBlock(unref(UiAvatar), {
2554
2571
  key: 0,
2555
- class: "size-8"
2572
+ class: "size-4 rounded-2xl bg-background-surface-muted-subtle"
2556
2573
  }, {
2557
2574
  default: withCtx(() => [
2558
2575
  createVNode(unref(UiAvatarImage), {
@@ -2561,23 +2578,27 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2561
2578
  }, null, 8, ["src", "alt"])
2562
2579
  ]),
2563
2580
  _: 1
2564
- })) : (openBlock(), createBlock(unref(UiSpinner), {
2565
- key: 1,
2566
- class: "size-8",
2567
- "aria-label": unref(labels).agentLoadingAvatarAlt
2568
- }, null, 8, ["aria-label"]))
2581
+ })) : (openBlock(), createElementBlock("div", _hoisted_1, [
2582
+ createVNode(unref(UiSpinner), {
2583
+ class: "size-4",
2584
+ "aria-label": unref(labels).agentLoadingAvatarAlt
2585
+ }, null, 8, ["aria-label"])
2586
+ ]))
2569
2587
  ]) : renderSlot(_ctx.$slots, "agent-message-icon", {
2570
2588
  key: 1,
2571
2589
  message
2572
2590
  }, () => [
2573
- createVNode(unref(UiAvatar), { class: "size-8" }, {
2591
+ createVNode(unref(UiAvatar), { class: "size-8 rounded-2xl bg-background-surface-muted-subtle" }, {
2574
2592
  default: withCtx(() => [
2575
2593
  __props.agentAvatarImage ? (openBlock(), createBlock(unref(UiAvatarImage), {
2576
2594
  key: 0,
2577
2595
  src: __props.agentAvatarImage,
2578
2596
  alt: unref(labels).agentAvatarAlt
2579
2597
  }, null, 8, ["src", "alt"])) : createCommentVNode("", true),
2580
- __props.agentAvatarFallback ? (openBlock(), createBlock(unref(UiAvatarFallback), { key: 1 }, {
2598
+ __props.agentAvatarFallback ? (openBlock(), createBlock(unref(UiAvatarFallback), {
2599
+ key: 1,
2600
+ class: "bg-background-surface-muted-subtle rounded-2xl"
2601
+ }, {
2581
2602
  default: withCtx(() => [
2582
2603
  createTextVNode(toDisplayString(__props.agentAvatarFallback), 1)
2583
2604
  ]),
@@ -2590,7 +2611,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2590
2611
  ]),
2591
2612
  key: "0"
2592
2613
  } : void 0,
2593
- _ctx.$slots["agent-message-header-activity"] ? {
2614
+ hasHeaderActivity(message) ? {
2594
2615
  name: "activity",
2595
2616
  fn: withCtx(() => [
2596
2617
  renderSlot(_ctx.$slots, "agent-message-header-activity", { message })
@@ -7,16 +7,11 @@ type Story = StoryObj<typeof meta>;
7
7
  * Drop-in conversation UI. Pass `messages` with `role`, `content` (a list of
8
8
  * text / tool / reasoning parts), and `status` fields and CkConversation
9
9
  * renders user bubbles, assistant markdown, avatars, and a copy button on
10
- * each reply.
10
+ * each reply. Here the assistant messages carry a `reasoning` part whose
11
+ * `text` is surfaced as the header (e.g. "Thought about …") via
12
+ * `#agent-message-header-content`.
11
13
  */
12
14
  export declare const Default: Story;
13
- /**
14
- * Demonstrates a chain-of-thought assistant turn with reasoning and tool
15
- * calls in `message.content`. By default only text parts are rendered, so
16
- * use `#agent-message-content` to opt-in to a custom rendering of the
17
- * non-text parts. The host owns the visual treatment for those segments.
18
- */
19
- export declare const ChainOfThought: Story;
20
15
  /**
21
16
  * Uses a remote avatar image (with fallback text). Use when you want a
22
17
  * branded agent identity in each reply.
@@ -1 +1 @@
1
- {"version":3,"file":"CkConversation.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/composed/CkConversation/CkConversation.stories.ts"],"names":[],"mappings":"AAQA,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAiH3D,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,cAAc,CAkCrC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAqTnC;;;;;GAKG;AACH,eAAO,MAAM,OAAO,EAAE,KAUrB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,KAyB5B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,QAAQ,EAAE,KAUtB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,KAYtC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,KASpB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,KAe5B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAmC/B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,KAiBnC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,KAiBpC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,KA4GjC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,KAmBvC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAoDjC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,KAMnB,CAAC"}
1
+ {"version":3,"file":"CkConversation.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/composed/CkConversation/CkConversation.stories.ts"],"names":[],"mappings":"AASA,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AA4G3D,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,cAAc,CAkCrC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAiRnC;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,EAAE,KAerB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,QAAQ,EAAE,KAUtB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,KAYtC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,KASpB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,KAe5B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAmC/B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,KAiBnC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,KAiBpC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,KA4GjC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,KAmBvC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAoDjC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,KAMnB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"CkConversation.vue.d.ts","sourceRoot":"","sources":["../../../../src/components/composed/CkConversation/CkConversation.vue"],"names":[],"mappings":"AA6MA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,gBAAgB,CAAC;AAOtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAkBnD,KAAK,WAAW,GAAG;IACjB,gDAAgD;IAChD,cAAc,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAC1D,yDAAyD;IACzD,sBAAsB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAClE,iDAAiD;IACjD,eAAe,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3D,oCAAoC;IACpC,oBAAoB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAChE,8EAA8E;IAC9E,4BAA4B,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IACxE,iFAAiF;IACjF,sBAAsB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAClE,6EAA6E;IAC7E,8BAA8B,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAC1E;;;;OAIG;IACH,+BAA+B,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E;;;;OAIG;IACH,uBAAuB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IACnE,mDAAmD;IACnD,sBAAsB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAClE,qEAAqE;IACrE,0BAA0B,EAAE,CAAC,KAAK,EAAE;QAClC,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KAClB,KAAK,IAAI,CAAC;CACZ,CAAC;AAsUF,QAAA,MAAM,UAAU;;;;6EAGd,CAAC;AACH,QAAA,MAAM,YAAY,EAAS,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC,CAAC;wBACtD,OAAO,YAAY;AAAxC,wBAAyC;AAWzC,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
1
+ {"version":3,"file":"CkConversation.vue.d.ts","sourceRoot":"","sources":["../../../../src/components/composed/CkConversation/CkConversation.vue"],"names":[],"mappings":"AA6OA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,gBAAgB,CAAC;AAOtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAkBnD,KAAK,WAAW,GAAG;IACjB,gDAAgD;IAChD,cAAc,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAC1D,yDAAyD;IACzD,sBAAsB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAClE,iDAAiD;IACjD,eAAe,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3D,oCAAoC;IACpC,oBAAoB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAChE,8EAA8E;IAC9E,4BAA4B,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IACxE,iFAAiF;IACjF,sBAAsB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAClE,6EAA6E;IAC7E,8BAA8B,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAC1E;;;;OAIG;IACH,+BAA+B,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E;;;;OAIG;IACH,uBAAuB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IACnE,mDAAmD;IACnD,sBAAsB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAClE,qEAAqE;IACrE,0BAA0B,EAAE,CAAC,KAAK,EAAE;QAClC,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KAClB,KAAK,IAAI,CAAC;CACZ,CAAC;AA+WF,QAAA,MAAM,UAAU;;;;6EAGd,CAAC;AACH,QAAA,MAAM,YAAY,EAAS,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC,CAAC;wBACtD,OAAO,YAAY;AAAxC,wBAAyC;AAWzC,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
@@ -1,4 +1,4 @@
1
- import { _, a, C, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y } from "../CkHistory.vue_vue_type_script_setup_true_lang-CtQUc3m3.js";
1
+ import { _, a, C, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y } from "../CkHistory.vue_vue_type_script_setup_true_lang-vlUZ_Tw1.js";
2
2
  export {
3
3
  _ as CkAgentMessage,
4
4
  a as CkAgentMessageHeader,
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { u } from "./useMessageHandler-N50Yj-NU.js";
2
2
  import { C, d, p, u as u2, a } from "./useChatKitLabels-zMFV7aEz.js";
3
3
  import { S, c, d as d2, a as a2, u as u3, b, e, f } from "./index-CyrVnbS9.js";
4
- import { _, a as a3, C as C2, b as b2, c as c2, d as d3, e as e2, f as f2, g, h, i, j, k, l, m, n, o, p as p2, q, r, s, t, u as u4, v, w, x, y, z } from "./CkHistory.vue_vue_type_script_setup_true_lang-CtQUc3m3.js";
4
+ import { _, a as a3, C as C2, b as b2, c as c2, d as d3, e as e2, f as f2, g, h, i, j, k, l, m, n, o, p as p2, q, r, s, t, u as u4, v, w, x, y, z } from "./CkHistory.vue_vue_type_script_setup_true_lang-vlUZ_Tw1.js";
5
5
  export {
6
6
  C as CHAT_KIT_LABELS,
7
7
  _ as CkAgentMessage,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aleph-alpha/chat-kit",
3
- "version": "5.0.1",
3
+ "version": "5.1.0",
4
4
  "description": "Chat kit package for building chat interfaces with Vue",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -91,6 +91,8 @@
91
91
  "@unocss/reset": "^66.6.2",
92
92
  "@vitejs/plugin-vue": "^6.0.3",
93
93
  "@vue/test-utils": "^2.4.6",
94
+ "concurrently": "9.2.1",
95
+ "http-server": "14.1.1",
94
96
  "jsdom": "^25.0.1",
95
97
  "storybook": "^10.2.17",
96
98
  "pinia": "^3.0.3",
@@ -102,9 +104,10 @@
102
104
  "vitest": "3.2.6",
103
105
  "vue": "^3.5.27",
104
106
  "vue-tsc": "^3.2.4",
107
+ "wait-on": "9.0.3",
105
108
  "@aleph-alpha/eslint-config-frontend": "0.7.0",
106
- "@aleph-alpha/prettier-config-frontend": "2.1.0",
107
- "@aleph-alpha/tsconfig-frontend": "0.7.0"
109
+ "@aleph-alpha/tsconfig-frontend": "0.7.0",
110
+ "@aleph-alpha/prettier-config-frontend": "2.1.0"
108
111
  },
109
112
  "scripts": {
110
113
  "build": "vite build && vue-tsc --project tsconfig.build.json --declaration --emitDeclarationOnly --outDir dist",
@@ -114,6 +117,8 @@
114
117
  "format": "prettier --write .",
115
118
  "lint": "prettier --check . && eslint --fix src",
116
119
  "storybook": "storybook dev -p 6007",
117
- "build-storybook": "storybook build"
120
+ "build-storybook": "storybook build",
121
+ "test:storybook": "pnpm concurrently -k --ks SIGKILL -s command-test -n \"storybook,test\" \"pnpm http-server ../../dist/storybook/chat-kit --port 4401 --silent\" \"wait-on tcp:4401 && test-storybook --url http://127.0.0.1:4401\"",
122
+ "test:storybook:local": "pnpm build-storybook && pnpm concurrently -k --ks SIGKILL -s command-test -n \"storybook,test\" \"pnpm http-server storybook-static --port 4401 --silent\" \"wait-on tcp:4401 && test-storybook --url http://127.0.0.1:4401\""
118
123
  }
119
124
  }
@@ -2,7 +2,7 @@ import type { BaseMessage, MessageContent } from '../../../types';
2
2
  import CkConversation from './CkConversation.vue';
3
3
  import { defaultMarkdownClasses } from '../../base/CkMarkdownRenderer/defaultMarkdownClasses';
4
4
  import { render, screen } from '@testing-library/vue';
5
- import { h } from 'vue';
5
+ import { createCommentVNode, h } from 'vue';
6
6
 
7
7
  const now = new Date();
8
8
 
@@ -404,6 +404,37 @@ describe('CkConversation', () => {
404
404
  expect(screen.getByTestId('activity')).toBeInTheDocument();
405
405
  });
406
406
 
407
+ it('shows the header divider when the activity slot renders content', () => {
408
+ render(CkConversation, {
409
+ props: { messages: [agentMessage()], agentAvatarFallback: 'AI' },
410
+ slots: {
411
+ 'agent-message-header-activity': () =>
412
+ h('button', { 'data-testid': 'activity' }, '1 tool'),
413
+ },
414
+ });
415
+
416
+ expect(screen.getByTestId('activity')).toBeInTheDocument();
417
+ expect(
418
+ screen.getByTestId('ck-agent-message-header-row').className,
419
+ ).toContain('border-b');
420
+ });
421
+
422
+ it('omits the header divider when the activity slot renders nothing for a message', () => {
423
+ render(CkConversation, {
424
+ props: { messages: [agentMessage()], agentAvatarFallback: 'AI' },
425
+ slots: {
426
+ // Emulates a falsy `v-if` inside the slot (renders a comment node).
427
+ 'agent-message-header-activity': () => createCommentVNode('v-if'),
428
+ },
429
+ });
430
+
431
+ // The header still renders because of the avatar icon...
432
+ const row = screen.getByTestId('ck-agent-message-header-row');
433
+ // ...but there is no activity control and no divider.
434
+ expect(screen.queryByTestId('activity')).not.toBeInTheDocument();
435
+ expect(row.className).not.toContain('border-b');
436
+ });
437
+
407
438
  it('allows adding footer actions via agent-message-footer slot', () => {
408
439
  render(CkConversation, {
409
440
  props: { messages: [agentMessage()] },
@@ -4,6 +4,7 @@ import type { CkFeedbackValue } from '../../base/CkFeedbackButton';
4
4
  import type {
5
5
  BaseMessage,
6
6
  MessageContent,
7
+ MessageReasoningContent,
7
8
  MessageTextContent,
8
9
  } from '../../../types';
9
10
  import CkConversation from './CkConversation.vue';
@@ -30,6 +31,15 @@ function getMessageText(message: BaseMessage): string {
30
31
  .join('');
31
32
  }
32
33
 
34
+ /** First reasoning trail found on a message, used as the header text. */
35
+ function getReasoningText(message: BaseMessage): string {
36
+ return (
37
+ message.content.find(
38
+ (c): c is MessageReasoningContent => c.type === 'reasoning',
39
+ )?.text ?? ''
40
+ );
41
+ }
42
+
33
43
  const markdownContent = [
34
44
  'Based on a review of internal documents, here are the **key lessons learned** and best practices from the *Digital Transformation Phase 1* project, broken down by category.',
35
45
  '',
@@ -67,6 +77,12 @@ const sampleMessages: BaseMessage[] = [
67
77
  id: '2',
68
78
  role: 'assistant',
69
79
  content: [
80
+ {
81
+ type: 'reasoning',
82
+ id: 'r-2',
83
+ status: 'completed',
84
+ text: 'Thought about how best to help',
85
+ },
70
86
  text('Of course! I am here to help. What would you like to know?'),
71
87
  ],
72
88
  createdAt: now,
@@ -86,38 +102,18 @@ const sampleMessages: BaseMessage[] = [
86
102
  {
87
103
  id: '4',
88
104
  role: 'assistant',
89
- content: [text(markdownContent)],
90
- createdAt: now,
91
- status: 'completed',
92
- },
93
- ];
94
-
95
- const chainOfThoughtMessages: BaseMessage[] = [
96
- {
97
- id: '1',
98
- role: 'user',
99
105
  content: [
100
- text(
101
- 'Look up the recent revenue numbers for Q3 and tell me how they compare to last year.',
102
- ),
106
+ {
107
+ type: 'reasoning',
108
+ id: 'r-4',
109
+ status: 'completed',
110
+ text: 'Thought about the lessons learned of the Digital Transformation Phase 1',
111
+ },
112
+ text(markdownContent),
103
113
  ],
104
114
  createdAt: now,
105
115
  status: 'completed',
106
116
  },
107
- {
108
- id: '2',
109
- role: 'assistant',
110
- createdAt: now,
111
- status: 'completed',
112
- content: [
113
- { type: 'reasoning', id: 'r-1', status: 'completed' },
114
- { type: 'tool', id: 't-1', name: 'finance_lookup' },
115
- { type: 'tool', id: 't-2', name: 'compare_periods' },
116
- text(
117
- '## Revenue summary\n\n- **Q3 2025:** **$48.2M** (+12% YoY)\n- **Q3 2024:** $43.0M\n\nThe biggest contributor was the EMEA segment, which grew **18%** year over year. North America was roughly flat.',
118
- ),
119
- ],
120
- },
121
117
  ];
122
118
 
123
119
  const meta: Meta<typeof CkConversation> = {
@@ -174,63 +170,27 @@ const messages: BaseMessage[] = [
174
170
  {
175
171
  id: '2',
176
172
  role: 'assistant',
177
- content: [{ type: 'text', id: 't2', text: 'Hi there!', status: 'completed' }],
173
+ content: [
174
+ // A \`reasoning\` part carries the thinking trail shown in the header.
175
+ { type: 'reasoning', id: 'r2', text: 'Thought about how best to help', status: 'completed' },
176
+ { type: 'text', id: 't2', text: 'Hi there!', status: 'completed' },
177
+ ],
178
178
  createdAt: new Date(),
179
179
  status: 'completed',
180
180
  },
181
181
  ];
182
- </script>
183
182
 
184
- <template>
185
- <CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;" />
186
- </template>`;
187
-
188
- const chainOfThoughtTemplateSource = `<script setup lang="ts">
189
- import { CkConversation } from '@aleph-alpha/chat-kit';
190
- import type { BaseMessage } from '@aleph-alpha/chat-kit';
191
-
192
- const messages: BaseMessage[] = [
193
- {
194
- id: '1',
195
- role: 'user',
196
- content: [{ type: 'text', id: 't1', text: 'Compare Q3 revenue.', status: 'completed' }],
197
- createdAt: new Date(),
198
- status: 'completed',
199
- },
200
- {
201
- id: '2',
202
- role: 'assistant',
203
- createdAt: new Date(),
204
- status: 'completed',
205
- content: [
206
- { type: 'reasoning', id: 'r1', status: 'completed' },
207
- { type: 'tool', id: 'tl1', name: 'finance_lookup' },
208
- { type: 'tool', id: 'tl2', name: 'compare_periods' },
209
- { type: 'text', id: 't2', text: 'Q3 grew **12% YoY**.', status: 'completed' },
210
- ],
211
- },
212
- ];
183
+ // Pull the reasoning trail off the message so it can be shown in the header.
184
+ function getReasoningText(message: BaseMessage): string {
185
+ return message.content.find((c) => c.type === 'reasoning')?.text ?? '';
186
+ }
213
187
  </script>
214
188
 
215
189
  <template>
216
190
  <CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
217
- <!--
218
- Use #agent-message-content to take over rendering of the entire body.
219
- The slot exposes the full message so you can iterate every content
220
- part (text, tool, reasoning) and render them however you like.
221
- -->
222
- <template #agent-message-content="{ message }">
223
- <div class="flex flex-col gap-2">
224
- <template v-for="part in message.content" :key="part.id">
225
- <span v-if="part.type === 'reasoning'" class="text-xs italic opacity-60">
226
- Reasoning: {{ part.status }}
227
- </span>
228
- <span v-else-if="part.type === 'tool'" class="text-xs font-mono opacity-70">
229
- ⚙️ {{ part.name }}
230
- </span>
231
- <p v-else-if="part.type === 'text'">{{ part.text }}</p>
232
- </template>
233
- </div>
191
+ <!-- Surface the reasoning trail from the message data as the header. -->
192
+ <template #agent-message-header-content="{ message }">
193
+ {{ getReasoningText(message) }}
234
194
  </template>
235
195
  </CkConversation>
236
196
  </template>`;
@@ -470,51 +430,25 @@ import { CkConversation } from '@aleph-alpha/chat-kit';
470
430
  * Drop-in conversation UI. Pass `messages` with `role`, `content` (a list of
471
431
  * text / tool / reasoning parts), and `status` fields and CkConversation
472
432
  * renders user bubbles, assistant markdown, avatars, and a copy button on
473
- * each reply.
433
+ * each reply. Here the assistant messages carry a `reasoning` part whose
434
+ * `text` is surfaced as the header (e.g. "Thought about …") via
435
+ * `#agent-message-header-content`.
474
436
  */
475
437
  export const Default: Story = {
476
438
  render: () => ({
477
439
  components: { CkConversation },
478
440
  setup() {
479
- return { messages: sampleMessages };
480
- },
481
- template:
482
- '<CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;" />',
483
- }),
484
- parameters: { docs: { source: { code: defaultTemplateSource } } },
485
- };
486
-
487
- /**
488
- * Demonstrates a chain-of-thought assistant turn with reasoning and tool
489
- * calls in `message.content`. By default only text parts are rendered, so
490
- * use `#agent-message-content` to opt-in to a custom rendering of the
491
- * non-text parts. The host owns the visual treatment for those segments.
492
- */
493
- export const ChainOfThought: Story = {
494
- render: () => ({
495
- components: { CkConversation },
496
- setup() {
497
- return { messages: chainOfThoughtMessages };
441
+ return { messages: sampleMessages, getReasoningText };
498
442
  },
499
443
  template: `
500
444
  <CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
501
- <template #agent-message-content="{ message }">
502
- <div style="display: flex; flex-direction: column; gap: 8px;">
503
- <template v-for="part in message.content" :key="part.id">
504
- <span v-if="part.type === 'reasoning'" style="font-size: 12px; font-style: italic; opacity: 0.6;">
505
- Reasoning: {{ part.status }}
506
- </span>
507
- <span v-else-if="part.type === 'tool'" style="font-size: 12px; font-family: monospace; opacity: 0.7;">
508
- ⚙️ {{ part.name }}
509
- </span>
510
- <p v-else-if="part.type === 'text'" style="margin: 0;">{{ part.text }}</p>
511
- </template>
512
- </div>
445
+ <template #agent-message-header-content="{ message }">
446
+ {{ getReasoningText(message) }}
513
447
  </template>
514
448
  </CkConversation>
515
449
  `,
516
450
  }),
517
- parameters: { docs: { source: { code: chainOfThoughtTemplateSource } } },
451
+ parameters: { docs: { source: { code: defaultTemplateSource } } },
518
452
  };
519
453
 
520
454
  /**
@@ -14,7 +14,7 @@ import {
14
14
  UiAvatarImage,
15
15
  UiSpinner,
16
16
  } from '@aleph-alpha/ui-library';
17
- import { computed } from 'vue';
17
+ import { Comment, Fragment, Text, computed, type VNode } from 'vue';
18
18
 
19
19
  defineOptions({ name: 'CkConversation' });
20
20
 
@@ -92,6 +92,30 @@ function getMessageText(message: BaseMessage): string {
92
92
  .map((c) => c.text)
93
93
  .join('');
94
94
  }
95
+
96
+ /** Whether a rendered VNode tree is effectively empty (e.g. a falsy `v-if`). */
97
+ function isVNodeEmpty(vnode: VNode): boolean {
98
+ if (vnode.type === Comment) return true;
99
+ if (vnode.type === Text) return String(vnode.children ?? '').trim() === '';
100
+ if (vnode.type === Fragment) {
101
+ const children = vnode.children as VNode[] | null;
102
+ return !children || children.every(isVNodeEmpty);
103
+ }
104
+ return false;
105
+ }
106
+
107
+ /**
108
+ * Whether the `agent-message-header-activity` slot renders real content for
109
+ * this message. Gates both the activity control and the header divider, so a
110
+ * host can toggle them per message with a `v-if` inside the slot instead of
111
+ * the divider being forced on for every message once the slot is defined.
112
+ */
113
+ function hasHeaderActivity(message: BaseMessage): boolean {
114
+ const slot = slots['agent-message-header-activity'] as
115
+ | ((props: { message: BaseMessage }) => VNode[])
116
+ | undefined;
117
+ return slot ? slot({ message }).some((vnode) => !isVNodeEmpty(vnode)) : false;
118
+ }
95
119
  </script>
96
120
 
97
121
  <template>
@@ -116,7 +140,7 @@ function getMessageText(message: BaseMessage): string {
116
140
  hasIcon ||
117
141
  $slots['agent-message-header'] ||
118
142
  $slots['agent-message-header-content'] ||
119
- $slots['agent-message-header-activity']
143
+ hasHeaderActivity(message)
120
144
  "
121
145
  #header
122
146
  >
@@ -125,31 +149,42 @@ function getMessageText(message: BaseMessage): string {
125
149
  <template v-if="hasIcon" #icon>
126
150
  <template v-if="isLoading(message)">
127
151
  <slot name="agent-message-loading-icon" :message="message">
128
- <UiAvatar v-if="agentLoadingImage" class="size-8">
152
+ <UiAvatar
153
+ v-if="agentLoadingImage"
154
+ class="size-4 rounded-2xl bg-background-surface-muted-subtle"
155
+ >
129
156
  <UiAvatarImage
130
157
  :src="agentLoadingImage"
131
158
  :alt="labels.agentLoadingAvatarAlt"
132
159
  />
133
160
  </UiAvatar>
134
- <UiSpinner
161
+ <div
135
162
  v-else
136
- class="size-8"
137
- :aria-label="labels.agentLoadingAvatarAlt"
138
- />
163
+ class="flex size-8 shrink-0 items-center justify-center rounded-2xl bg-background-surface-muted-subtle"
164
+ >
165
+ <UiSpinner
166
+ class="size-4"
167
+ :aria-label="labels.agentLoadingAvatarAlt"
168
+ />
169
+ </div>
139
170
  </slot>
140
171
  </template>
141
172
 
142
173
  <template v-else>
143
174
  <slot name="agent-message-icon" :message="message">
144
- <UiAvatar class="size-8">
175
+ <UiAvatar
176
+ class="size-8 rounded-2xl bg-background-surface-muted-subtle"
177
+ >
145
178
  <UiAvatarImage
146
179
  v-if="agentAvatarImage"
147
180
  :src="agentAvatarImage"
148
181
  :alt="labels.agentAvatarAlt"
149
182
  />
150
- <UiAvatarFallback v-if="agentAvatarFallback">{{
151
- agentAvatarFallback
152
- }}</UiAvatarFallback>
183
+ <UiAvatarFallback
184
+ v-if="agentAvatarFallback"
185
+ class="bg-background-surface-muted-subtle rounded-2xl"
186
+ >{{ agentAvatarFallback }}</UiAvatarFallback
187
+ >
153
188
  </UiAvatar>
154
189
  </slot>
155
190
  </template>
@@ -157,10 +192,7 @@ function getMessageText(message: BaseMessage): string {
157
192
 
158
193
  <slot name="agent-message-header-content" :message="message" />
159
194
 
160
- <template
161
- v-if="$slots['agent-message-header-activity']"
162
- #activity
163
- >
195
+ <template v-if="hasHeaderActivity(message)" #activity>
164
196
  <slot
165
197
  name="agent-message-header-activity"
166
198
  :message="message"