@apteva/apteva-kit 0.1.69 → 0.1.71

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/index.d.mts CHANGED
@@ -23,6 +23,8 @@ interface Widget {
23
23
  actions?: Action[];
24
24
  /** Additional data not displayed but captured via onWidgetRender */
25
25
  meta?: Record<string, any>;
26
+ /** Whether the widget is still receiving streaming data */
27
+ isStreaming?: boolean;
26
28
  }
27
29
  interface CardWidget extends Widget {
28
30
  type: 'card';
@@ -392,7 +394,9 @@ interface CardProps {
392
394
  declare function Card({ widget, onAction }: CardProps): react_jsx_runtime.JSX.Element;
393
395
 
394
396
  interface ListProps {
395
- widget: ListWidget;
397
+ widget: ListWidget & {
398
+ isStreaming?: boolean;
399
+ };
396
400
  onAction?: (action: ActionEvent) => void;
397
401
  }
398
402
  declare function List({ widget, onAction }: ListProps): react_jsx_runtime.JSX.Element;
package/dist/index.d.ts CHANGED
@@ -23,6 +23,8 @@ interface Widget {
23
23
  actions?: Action[];
24
24
  /** Additional data not displayed but captured via onWidgetRender */
25
25
  meta?: Record<string, any>;
26
+ /** Whether the widget is still receiving streaming data */
27
+ isStreaming?: boolean;
26
28
  }
27
29
  interface CardWidget extends Widget {
28
30
  type: 'card';
@@ -392,7 +394,9 @@ interface CardProps {
392
394
  declare function Card({ widget, onAction }: CardProps): react_jsx_runtime.JSX.Element;
393
395
 
394
396
  interface ListProps {
395
- widget: ListWidget;
397
+ widget: ListWidget & {
398
+ isStreaming?: boolean;
399
+ };
396
400
  onAction?: (action: ActionEvent) => void;
397
401
  }
398
402
  declare function List({ widget, onAction }: ListProps): react_jsx_runtime.JSX.Element;
package/dist/index.js CHANGED
@@ -502,6 +502,71 @@ function validateFile(file) {
502
502
  }
503
503
 
504
504
  // src/utils/widget-parser.ts
505
+ var STREAMABLE_WIDGET_TYPES = ["list", "table"];
506
+ function parsePartialItemsArray(partialJson) {
507
+ const items = [];
508
+ let isStreaming = false;
509
+ const itemsMatch = partialJson.match(/"items"\s*:\s*\[/);
510
+ if (!itemsMatch) {
511
+ return { items, isStreaming: false };
512
+ }
513
+ const arrayStart = partialJson.indexOf("[", itemsMatch.index);
514
+ if (arrayStart === -1) {
515
+ return { items, isStreaming: true };
516
+ }
517
+ let depth = 0;
518
+ let inString = false;
519
+ let escapeNext = false;
520
+ let objectStart = -1;
521
+ for (let i = arrayStart + 1; i < partialJson.length; i++) {
522
+ const char = partialJson[i];
523
+ if (escapeNext) {
524
+ escapeNext = false;
525
+ continue;
526
+ }
527
+ if (char === "\\" && inString) {
528
+ escapeNext = true;
529
+ continue;
530
+ }
531
+ if (char === '"') {
532
+ inString = !inString;
533
+ continue;
534
+ }
535
+ if (inString) continue;
536
+ if (char === "{") {
537
+ if (depth === 0) {
538
+ objectStart = i;
539
+ }
540
+ depth++;
541
+ } else if (char === "}") {
542
+ depth--;
543
+ if (depth === 0 && objectStart !== -1) {
544
+ const objectJson = partialJson.slice(objectStart, i + 1);
545
+ try {
546
+ const item = JSON.parse(objectJson);
547
+ if (!item.id) {
548
+ item.id = `item-${items.length}-${simpleHash(objectJson)}`;
549
+ }
550
+ items.push(item);
551
+ } catch (e) {
552
+ }
553
+ objectStart = -1;
554
+ }
555
+ } else if (char === "]" && depth === 0) {
556
+ isStreaming = false;
557
+ break;
558
+ }
559
+ }
560
+ if (depth > 0 || objectStart !== -1) {
561
+ isStreaming = true;
562
+ }
563
+ const afterItems = partialJson.slice(arrayStart);
564
+ const closingBracket = findMatchingBracket(afterItems, 0);
565
+ if (closingBracket === -1) {
566
+ isStreaming = true;
567
+ }
568
+ return { items, isStreaming };
569
+ }
505
570
  function simpleHash(str) {
506
571
  let hash = 0;
507
572
  for (let i = 0; i < str.length; i++) {
@@ -563,9 +628,38 @@ function parseWidgetsFromText(text) {
563
628
  const fullBracketStart = lastWidgetStart + bracketOpenIndex;
564
629
  const bracketEnd = findMatchingBracket(text, fullBracketStart);
565
630
  if (bracketEnd === -1) {
566
- processText = text.slice(0, lastWidgetStart);
567
- pendingWidgetType = widgetType;
568
- hasPendingWidget = true;
631
+ if (STREAMABLE_WIDGET_TYPES.includes(widgetType)) {
632
+ const partialContent = text.slice(fullBracketStart + 1);
633
+ const { items, isStreaming } = parsePartialItemsArray(partialContent);
634
+ if (items.length > 0) {
635
+ processText = text.slice(0, lastWidgetStart);
636
+ const widgetId = `widget-${widgetType}-streaming-${simpleHash(partialContent)}`;
637
+ const textBefore = processText.replace(/[\s:;\-–—\.]+$/g, "").trim();
638
+ if (textBefore) {
639
+ segments.push({ type: "text", content: textBefore });
640
+ }
641
+ segments.push({
642
+ type: "widget",
643
+ widget: {
644
+ type: widgetType,
645
+ id: widgetId,
646
+ props: widgetType === "table" ? { rows: items, columns: [] } : { items },
647
+ isStreaming: true
648
+ }
649
+ });
650
+ hasWidgets = true;
651
+ hasPendingWidget = false;
652
+ processText = "";
653
+ } else {
654
+ processText = text.slice(0, lastWidgetStart);
655
+ pendingWidgetType = widgetType;
656
+ hasPendingWidget = true;
657
+ }
658
+ } else {
659
+ processText = text.slice(0, lastWidgetStart);
660
+ pendingWidgetType = widgetType;
661
+ hasPendingWidget = true;
662
+ }
569
663
  }
570
664
  }
571
665
  }
@@ -729,38 +823,73 @@ function Card({ widget, onAction }) {
729
823
 
730
824
  // src/components/Widgets/widget-library/List.tsx
731
825
 
826
+
732
827
  function List({ widget, onAction }) {
733
828
  const { items } = widget.props;
734
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "border border-neutral-200 dark:border-neutral-700 rounded-xl bg-white dark:bg-neutral-900 overflow-hidden", children: items.map((item, index) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
735
- "div",
736
- {
737
- className: `flex items-center p-4 transition-colors ${index !== items.length - 1 ? "border-b border-neutral-200 dark:border-neutral-700" : ""} ${!item.backgroundColor ? "hover:bg-neutral-50 dark:hover:bg-neutral-800" : ""}`,
738
- style: item.backgroundColor ? { backgroundColor: item.backgroundColor } : void 0,
739
- children: [
740
- item.image && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "img", { src: item.image, alt: item.title, className: "w-16 h-16 rounded object-cover" }),
741
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: `flex-1 ${item.image ? "ml-4" : ""}`, children: [
742
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h4", { className: "font-semibold !text-neutral-900 dark:!text-white", children: item.title }),
743
- item.subtitle && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "!text-sm !text-neutral-600 dark:!text-neutral-400", children: item.subtitle }),
744
- item.description && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "!text-xs !text-neutral-500 dark:!text-neutral-500 mt-1", children: item.description })
745
- ] }),
746
- widget.actions && widget.actions.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex gap-2", children: widget.actions.map((action, idx) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
747
- "button",
748
- {
749
- onClick: () => _optionalChain([onAction, 'optionalCall', _3 => _3({
750
- type: action.type,
751
- payload: item.metadata || item,
752
- widgetId: widget.id,
753
- timestamp: /* @__PURE__ */ new Date()
754
- })]),
755
- className: "px-3 py-1.5 !text-sm rounded-lg font-medium transition-colors bg-blue-500 !text-white hover:bg-blue-600",
756
- children: action.label
757
- },
758
- idx
759
- )) })
760
- ]
761
- },
762
- item.id
763
- )) });
829
+ const isStreaming = _nullishCoalesce(widget.isStreaming, () => ( false));
830
+ const seenItemsRef = _react.useRef.call(void 0, /* @__PURE__ */ new Set());
831
+ const [newItemIds, setNewItemIds] = _react.useState.call(void 0, /* @__PURE__ */ new Set());
832
+ _react.useEffect.call(void 0, () => {
833
+ const currentIds = new Set(items.map((item) => item.id));
834
+ const newIds = /* @__PURE__ */ new Set();
835
+ items.forEach((item) => {
836
+ if (!seenItemsRef.current.has(item.id)) {
837
+ newIds.add(item.id);
838
+ }
839
+ });
840
+ items.forEach((item) => seenItemsRef.current.add(item.id));
841
+ if (newIds.size > 0) {
842
+ setNewItemIds(newIds);
843
+ const timer = setTimeout(() => {
844
+ setNewItemIds(/* @__PURE__ */ new Set());
845
+ }, 500);
846
+ return () => clearTimeout(timer);
847
+ }
848
+ }, [items]);
849
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "border border-neutral-200 dark:border-neutral-700 rounded-xl bg-white dark:bg-neutral-900 overflow-hidden", children: [
850
+ items.map((item, index) => {
851
+ const isNew = newItemIds.has(item.id);
852
+ const isLast = index === items.length - 1;
853
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
854
+ "div",
855
+ {
856
+ className: `apteva-list-item flex items-center p-4 transition-colors ${!isLast || isStreaming ? "border-b border-neutral-200 dark:border-neutral-700" : ""} ${!item.backgroundColor ? "hover:bg-neutral-50 dark:hover:bg-neutral-800" : ""} ${isNew ? "apteva-list-item-new" : ""}`,
857
+ style: item.backgroundColor ? { backgroundColor: item.backgroundColor } : void 0,
858
+ children: [
859
+ item.image && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "img", { src: item.image, alt: item.title, className: "w-16 h-16 rounded object-cover" }),
860
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: `flex-1 ${item.image ? "ml-4" : ""}`, children: [
861
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h4", { className: "font-semibold !text-neutral-900 dark:!text-white", children: item.title }),
862
+ item.subtitle && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "!text-sm !text-neutral-600 dark:!text-neutral-400", children: item.subtitle }),
863
+ item.description && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "!text-xs !text-neutral-500 dark:!text-neutral-500 mt-1", children: item.description })
864
+ ] }),
865
+ widget.actions && widget.actions.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex gap-2", children: widget.actions.map((action, idx) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
866
+ "button",
867
+ {
868
+ onClick: () => _optionalChain([onAction, 'optionalCall', _3 => _3({
869
+ type: action.type,
870
+ payload: item.metadata || item,
871
+ widgetId: widget.id,
872
+ timestamp: /* @__PURE__ */ new Date()
873
+ })]),
874
+ className: "px-3 py-1.5 !text-sm rounded-lg font-medium transition-colors bg-blue-500 !text-white hover:bg-blue-600",
875
+ children: action.label
876
+ },
877
+ idx
878
+ )) })
879
+ ]
880
+ },
881
+ item.id
882
+ );
883
+ }),
884
+ isStreaming && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "apteva-list-streaming flex items-center gap-3 p-4 text-neutral-500 dark:text-neutral-400", children: [
885
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "apteva-streaming-dots flex gap-1", children: [
886
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "w-2 h-2 bg-current rounded-full animate-pulse", style: { animationDelay: "0ms" } }),
887
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "w-2 h-2 bg-current rounded-full animate-pulse", style: { animationDelay: "150ms" } }),
888
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "w-2 h-2 bg-current rounded-full animate-pulse", style: { animationDelay: "300ms" } })
889
+ ] }),
890
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-sm", children: "Loading more..." })
891
+ ] })
892
+ ] });
764
893
  }
765
894
 
766
895
  // src/components/Widgets/widget-library/Button.tsx
@@ -840,8 +969,32 @@ function ButtonGroup({ widget, onAction }) {
840
969
 
841
970
  // src/components/Widgets/widget-library/Table.tsx
842
971
 
972
+
843
973
  function Table({ widget, onAction }) {
844
974
  const { columns, rows, caption, compact = false, striped = false } = widget.props;
975
+ const isStreaming = _nullishCoalesce(widget.isStreaming, () => ( false));
976
+ const seenRowsRef = _react.useRef.call(void 0, /* @__PURE__ */ new Set());
977
+ const [newRowIds, setNewRowIds] = _react.useState.call(void 0, /* @__PURE__ */ new Set());
978
+ _react.useEffect.call(void 0, () => {
979
+ const newIds = /* @__PURE__ */ new Set();
980
+ rows.forEach((row, index) => {
981
+ const rowId = row.id || `row-${index}`;
982
+ if (!seenRowsRef.current.has(rowId)) {
983
+ newIds.add(rowId);
984
+ }
985
+ });
986
+ rows.forEach((row, index) => {
987
+ const rowId = row.id || `row-${index}`;
988
+ seenRowsRef.current.add(rowId);
989
+ });
990
+ if (newIds.size > 0) {
991
+ setNewRowIds(newIds);
992
+ const timer = setTimeout(() => {
993
+ setNewRowIds(/* @__PURE__ */ new Set());
994
+ }, 500);
995
+ return () => clearTimeout(timer);
996
+ }
997
+ }, [rows]);
845
998
  const getAlignment = (align) => {
846
999
  switch (align) {
847
1000
  case "center":
@@ -868,47 +1021,67 @@ function Table({ widget, onAction }) {
868
1021
  column.key
869
1022
  )) }) }),
870
1023
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "tbody", { children: [
871
- rows.map((row, rowIndex) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
872
- "tr",
873
- {
874
- className: cn(
875
- "border-b border-neutral-200 dark:border-neutral-700 last:border-b-0",
876
- "transition-colors hover:bg-neutral-50 dark:hover:bg-neutral-800",
877
- striped && rowIndex % 2 === 1 && "bg-neutral-50/50 dark:bg-neutral-800/50"
878
- ),
879
- onClick: () => {
880
- if (widget.actions && widget.actions.length > 0) {
881
- _optionalChain([onAction, 'optionalCall', _15 => _15({
882
- type: widget.actions[0].type,
883
- payload: row,
884
- widgetId: widget.id,
885
- timestamp: /* @__PURE__ */ new Date()
886
- })]);
887
- }
888
- },
889
- style: { cursor: _optionalChain([widget, 'access', _16 => _16.actions, 'optionalAccess', _17 => _17.length]) ? "pointer" : "default" },
890
- children: columns.map((column) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
891
- "td",
892
- {
893
- className: cn(
894
- "text-neutral-700 dark:text-neutral-300",
895
- compact ? "px-3 py-2 text-xs" : "px-4 py-3 text-sm",
896
- getAlignment(column.align)
897
- ),
898
- children: _nullishCoalesce(row[column.key], () => ( "\u2014"))
1024
+ rows.map((row, rowIndex) => {
1025
+ const rowId = row.id || `row-${rowIndex}`;
1026
+ const isNew = newRowIds.has(rowId);
1027
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1028
+ "tr",
1029
+ {
1030
+ className: cn(
1031
+ "apteva-table-row border-b border-neutral-200 dark:border-neutral-700 last:border-b-0",
1032
+ "transition-colors hover:bg-neutral-50 dark:hover:bg-neutral-800",
1033
+ striped && rowIndex % 2 === 1 && "bg-neutral-50/50 dark:bg-neutral-800/50",
1034
+ isNew && "apteva-table-row-new"
1035
+ ),
1036
+ onClick: () => {
1037
+ if (widget.actions && widget.actions.length > 0) {
1038
+ _optionalChain([onAction, 'optionalCall', _15 => _15({
1039
+ type: widget.actions[0].type,
1040
+ payload: row,
1041
+ widgetId: widget.id,
1042
+ timestamp: /* @__PURE__ */ new Date()
1043
+ })]);
1044
+ }
899
1045
  },
900
- column.key
901
- ))
902
- },
903
- row.id || rowIndex
904
- )),
905
- rows.length === 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "tr", { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1046
+ style: { cursor: _optionalChain([widget, 'access', _16 => _16.actions, 'optionalAccess', _17 => _17.length]) ? "pointer" : "default" },
1047
+ children: columns.map((column) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1048
+ "td",
1049
+ {
1050
+ className: cn(
1051
+ "text-neutral-700 dark:text-neutral-300",
1052
+ compact ? "px-3 py-2 text-xs" : "px-4 py-3 text-sm",
1053
+ getAlignment(column.align)
1054
+ ),
1055
+ children: _nullishCoalesce(row[column.key], () => ( "\u2014"))
1056
+ },
1057
+ column.key
1058
+ ))
1059
+ },
1060
+ rowId
1061
+ );
1062
+ }),
1063
+ rows.length === 0 && !isStreaming && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "tr", { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
906
1064
  "td",
907
1065
  {
908
- colSpan: columns.length,
1066
+ colSpan: columns.length || 1,
909
1067
  className: "px-4 py-8 text-center text-sm text-neutral-500 dark:text-neutral-400",
910
1068
  children: "No data available"
911
1069
  }
1070
+ ) }),
1071
+ isStreaming && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "tr", { className: "apteva-table-streaming", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1072
+ "td",
1073
+ {
1074
+ colSpan: columns.length || 1,
1075
+ className: "px-4 py-3 text-center",
1076
+ children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center justify-center gap-3 text-neutral-500 dark:text-neutral-400", children: [
1077
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex gap-1", children: [
1078
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "w-2 h-2 bg-current rounded-full animate-pulse", style: { animationDelay: "0ms" } }),
1079
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "w-2 h-2 bg-current rounded-full animate-pulse", style: { animationDelay: "150ms" } }),
1080
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "w-2 h-2 bg-current rounded-full animate-pulse", style: { animationDelay: "300ms" } })
1081
+ ] }),
1082
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-sm", children: "Loading more..." })
1083
+ ] })
1084
+ }
912
1085
  ) })
913
1086
  ] })
914
1087
  ] }) }) });
@@ -1345,14 +1518,39 @@ function MarkdownContent({ content, className = "" }) {
1345
1518
 
1346
1519
  // src/components/Chat/ToolCall.tsx
1347
1520
 
1348
- function ToolCall({ name, status }) {
1521
+ function ToolCall({ name, status, isReceiving = false }) {
1522
+ if (status === "preparing") {
1523
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: `apteva-tool-card apteva-tool-card-preparing ${isReceiving ? "apteva-tool-receiving" : ""}`, children: [
1524
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "svg", { className: "apteva-tool-icon apteva-tool-icon-spin", fill: "none", viewBox: "0 0 24 24", children: [
1525
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "circle", { className: "apteva-tool-spinner-track", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
1526
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { className: "apteva-tool-spinner-fill", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })
1527
+ ] }),
1528
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "apteva-tool-label", children: [
1529
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: name }),
1530
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "apteva-tool-status-text", children: " preparing" }),
1531
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "apteva-tool-dots", children: [
1532
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: "." }),
1533
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: "." }),
1534
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: "." })
1535
+ ] })
1536
+ ] })
1537
+ ] });
1538
+ }
1349
1539
  if (status === "running") {
1350
1540
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "apteva-tool-card apteva-tool-card-running", children: [
1351
1541
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "svg", { className: "apteva-tool-icon apteva-tool-icon-spin", fill: "none", viewBox: "0 0 24 24", children: [
1352
1542
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "circle", { className: "apteva-tool-spinner-track", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
1353
1543
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { className: "apteva-tool-spinner-fill", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })
1354
1544
  ] }),
1355
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "apteva-tool-label", children: name })
1545
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "apteva-tool-label", children: [
1546
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: name }),
1547
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "apteva-tool-status-text", children: " running" }),
1548
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "apteva-tool-dots", children: [
1549
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: "." }),
1550
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: "." }),
1551
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: "." })
1552
+ ] })
1553
+ ] })
1356
1554
  ] });
1357
1555
  }
1358
1556
  if (status === "completed") {
@@ -1566,7 +1764,8 @@ function Message({ message, onAction, enableWidgets, onWidgetRender }) {
1566
1764
  ToolCall,
1567
1765
  {
1568
1766
  name: segment.name,
1569
- status: segment.result !== void 0 ? "completed" : "running"
1767
+ status: segment.status || (segment.result !== void 0 ? "completed" : "running"),
1768
+ isReceiving: segment.isReceiving
1570
1769
  }
1571
1770
  ) }, segment.id)
1572
1771
  );
@@ -2672,6 +2871,7 @@ ${widgetContext}` : widgetContext;
2672
2871
  let accumulatedWidgets = [];
2673
2872
  let responseThreadId = currentThreadId;
2674
2873
  let toolInputBuffer = "";
2874
+ let receivingTimeout = null;
2675
2875
  const streamingMessageId = `msg-${Date.now()}`;
2676
2876
  const updateMessage = () => {
2677
2877
  const segments = [...contentSegments];
@@ -2748,7 +2948,7 @@ ${widgetContext}` : widgetContext;
2748
2948
  contentSegments.push({ type: "text", content: currentTextBuffer });
2749
2949
  currentTextBuffer = "";
2750
2950
  }
2751
- contentSegments.push({ type: "tool", id: chunk.tool_id, name: displayName });
2951
+ contentSegments.push({ type: "tool", id: chunk.tool_id, name: displayName, status: "preparing" });
2752
2952
  toolInputBuffer = "";
2753
2953
  setChatToolName(displayName);
2754
2954
  _optionalChain([onToolCall, 'optionalCall', _72 => _72(chunk.tool_name, chunk.tool_id)]);
@@ -2758,6 +2958,28 @@ ${widgetContext}` : widgetContext;
2758
2958
  case "tool_input_delta":
2759
2959
  if (chunk.tool_id && chunk.content) {
2760
2960
  toolInputBuffer += chunk.content;
2961
+ const toolSegment = contentSegments.find((s) => s.type === "tool" && s.id === chunk.tool_id);
2962
+ if (toolSegment) {
2963
+ toolSegment.isReceiving = true;
2964
+ updateMessage();
2965
+ if (receivingTimeout) clearTimeout(receivingTimeout);
2966
+ receivingTimeout = setTimeout(() => {
2967
+ if (toolSegment.status === "preparing") {
2968
+ toolSegment.isReceiving = false;
2969
+ updateMessage();
2970
+ }
2971
+ }, 150);
2972
+ }
2973
+ }
2974
+ break;
2975
+ case "tool_use":
2976
+ if (chunk.tool_id) {
2977
+ const toolSegment = contentSegments.find((s) => s.type === "tool" && s.id === chunk.tool_id);
2978
+ if (toolSegment && toolSegment.status === "preparing") {
2979
+ toolSegment.status = "running";
2980
+ toolSegment.isReceiving = false;
2981
+ updateMessage();
2982
+ }
2761
2983
  }
2762
2984
  break;
2763
2985
  case "tool_result":
@@ -2765,6 +2987,8 @@ ${widgetContext}` : widgetContext;
2765
2987
  const toolSegment = contentSegments.find((s) => s.type === "tool" && s.id === chunk.tool_id);
2766
2988
  if (toolSegment) {
2767
2989
  toolSegment.result = chunk.content;
2990
+ toolSegment.status = "completed";
2991
+ toolSegment.isReceiving = false;
2768
2992
  _optionalChain([onToolResult, 'optionalCall', _73 => _73(toolSegment.name, chunk.content)]);
2769
2993
  }
2770
2994
  setChatToolName(null);