@xinghunm/ai-chat 0.2.0 → 0.2.1

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.js CHANGED
@@ -729,6 +729,64 @@ var getNextDisplayedUnitCount = ({
729
729
  var splitMarkdownBlocks = (content) => content.split(/\n{2,}/).map((block) => block.trim()).filter(Boolean);
730
730
 
731
731
  // src/components/chat-thread/hooks/use-chat-message-reveal.ts
732
+ var createRevealState = ({
733
+ isAssistantStreaming,
734
+ targetUnitCount
735
+ }) => {
736
+ const initialDisplayedUnitCount = isAssistantStreaming ? 0 : targetUnitCount;
737
+ return {
738
+ batchedTargetUnitCount: initialDisplayedUnitCount,
739
+ displayedUnitCount: initialDisplayedUnitCount,
740
+ isFreshBlockActive: false
741
+ };
742
+ };
743
+ var revealReducer = (state, action) => {
744
+ switch (action.type) {
745
+ case "reset-message":
746
+ return createRevealState(action);
747
+ case "commit-batched-target": {
748
+ const nextDisplayedUnitCount = action.role === "assistant" ? getNextDisplayedUnitCount({
749
+ currentUnits: state.displayedUnitCount,
750
+ targetUnits: action.nextTargetUnitCount,
751
+ isStreaming: action.isAssistantStreaming,
752
+ minimumStep: state.displayedUnitCount > 0 && action.isAssistantStreaming ? 2 : 1
753
+ }) : action.nextTargetUnitCount;
754
+ return {
755
+ ...state,
756
+ batchedTargetUnitCount: action.nextTargetUnitCount,
757
+ displayedUnitCount: nextDisplayedUnitCount
758
+ };
759
+ }
760
+ case "set-fresh-block-active":
761
+ return state.isFreshBlockActive === action.isActive ? state : {
762
+ ...state,
763
+ isFreshBlockActive: action.isActive
764
+ };
765
+ case "sync-displayed-target":
766
+ return state.displayedUnitCount === state.batchedTargetUnitCount ? state : {
767
+ ...state,
768
+ displayedUnitCount: state.batchedTargetUnitCount
769
+ };
770
+ case "advance-reveal": {
771
+ if (state.displayedUnitCount >= state.batchedTargetUnitCount) {
772
+ return state;
773
+ }
774
+ return {
775
+ ...state,
776
+ displayedUnitCount: Math.min(
777
+ state.batchedTargetUnitCount,
778
+ getNextDisplayedUnitCount({
779
+ currentUnits: state.displayedUnitCount,
780
+ targetUnits: state.batchedTargetUnitCount,
781
+ isStreaming: action.isAssistantStreaming
782
+ })
783
+ )
784
+ };
785
+ }
786
+ default:
787
+ return state;
788
+ }
789
+ };
732
790
  var useChatMessageReveal = (message) => {
733
791
  const isAssistantStreaming = message.role === "assistant" && message.status === "streaming";
734
792
  const targetContent = message.content || "";
@@ -736,41 +794,47 @@ var useChatMessageReveal = (message) => {
736
794
  const pendingTargetUnitCountRef = (0, import_react5.useRef)(targetUnits.length);
737
795
  const batchedTargetUnitCountRef = (0, import_react5.useRef)(isAssistantStreaming ? 0 : targetUnits.length);
738
796
  const inputBatchTimeoutRef = (0, import_react5.useRef)(null);
739
- const [batchedTargetUnitCount, setBatchedTargetUnitCount] = (0, import_react5.useState)(
740
- () => isAssistantStreaming ? 0 : targetUnits.length
741
- );
742
797
  const lastDisplayedBlockCountRef = (0, import_react5.useRef)(0);
743
- const [displayedUnitCount, setDisplayedUnitCount] = (0, import_react5.useState)(
744
- () => isAssistantStreaming ? 0 : targetUnits.length
798
+ const previousMessageIdRef = (0, import_react5.useRef)(message.id);
799
+ const [state, dispatch] = (0, import_react5.useReducer)(
800
+ revealReducer,
801
+ {
802
+ isAssistantStreaming,
803
+ targetUnitCount: targetUnits.length
804
+ },
805
+ createRevealState
745
806
  );
746
- const [isFreshBlockActive, setIsFreshBlockActive] = (0, import_react5.useState)(false);
807
+ const { batchedTargetUnitCount, displayedUnitCount, isFreshBlockActive } = state;
747
808
  const commitBatchedTargetUnitCount = (0, import_react5.useCallback)(
748
809
  (nextTargetUnitCount) => {
749
810
  batchedTargetUnitCountRef.current = nextTargetUnitCount;
750
- setBatchedTargetUnitCount(nextTargetUnitCount);
751
- setDisplayedUnitCount(
752
- (current) => message.role === "assistant" ? getNextDisplayedUnitCount({
753
- currentUnits: current,
754
- targetUnits: nextTargetUnitCount,
755
- isStreaming: isAssistantStreaming,
756
- minimumStep: current > 0 && isAssistantStreaming ? 2 : 1
757
- }) : nextTargetUnitCount
758
- );
811
+ dispatch({
812
+ type: "commit-batched-target",
813
+ isAssistantStreaming,
814
+ nextTargetUnitCount,
815
+ role: message.role
816
+ });
759
817
  },
760
818
  [isAssistantStreaming, message.role]
761
819
  );
762
820
  (0, import_react5.useEffect)(() => {
821
+ if (previousMessageIdRef.current === message.id) {
822
+ return;
823
+ }
824
+ previousMessageIdRef.current = message.id;
763
825
  pendingTargetUnitCountRef.current = targetUnits.length;
764
826
  batchedTargetUnitCountRef.current = isAssistantStreaming ? 0 : targetUnits.length;
765
- setBatchedTargetUnitCount(batchedTargetUnitCountRef.current);
766
- setDisplayedUnitCount(isAssistantStreaming ? 0 : targetUnits.length);
767
827
  lastDisplayedBlockCountRef.current = 0;
768
828
  if (inputBatchTimeoutRef.current !== null) {
769
829
  window.clearTimeout(inputBatchTimeoutRef.current);
770
830
  inputBatchTimeoutRef.current = null;
771
831
  }
772
- setIsFreshBlockActive(false);
773
- }, [message.id, isAssistantStreaming, targetUnits.length]);
832
+ dispatch({
833
+ type: "reset-message",
834
+ isAssistantStreaming,
835
+ targetUnitCount: targetUnits.length
836
+ });
837
+ }, [isAssistantStreaming, message.id, targetUnits.length]);
774
838
  (0, import_react5.useEffect)(() => {
775
839
  pendingTargetUnitCountRef.current = targetUnits.length;
776
840
  if (message.role !== "assistant" || !isAssistantStreaming) {
@@ -813,9 +877,9 @@ var useChatMessageReveal = (message) => {
813
877
  if (!hasNewDisplayedBlock) {
814
878
  return;
815
879
  }
816
- setIsFreshBlockActive(true);
880
+ dispatch({ type: "set-fresh-block-active", isActive: true });
817
881
  const timer = window.setTimeout(() => {
818
- setIsFreshBlockActive(false);
882
+ dispatch({ type: "set-fresh-block-active", isActive: false });
819
883
  }, STREAM_FRESH_BLOCK_SETTLE_MS);
820
884
  return () => {
821
885
  window.clearTimeout(timer);
@@ -825,32 +889,19 @@ var useChatMessageReveal = (message) => {
825
889
  const shouldAnimateReveal = message.role === "assistant" && displayedUnitCount < batchedTargetUnitCount && (isAssistantStreaming || displayedUnitCount > 0);
826
890
  if (!shouldAnimateReveal) {
827
891
  if (displayedUnitCount !== batchedTargetUnitCount) {
828
- setDisplayedUnitCount(batchedTargetUnitCount);
892
+ dispatch({ type: "sync-displayed-target" });
829
893
  }
830
894
  return;
831
895
  }
832
896
  const timer = window.setInterval(() => {
833
- setDisplayedUnitCount((current) => {
834
- if (current >= batchedTargetUnitCount) {
835
- window.clearInterval(timer);
836
- return current;
837
- }
838
- return Math.min(
839
- batchedTargetUnitCount,
840
- getNextDisplayedUnitCount({
841
- currentUnits: current,
842
- targetUnits: batchedTargetUnitCount,
843
- isStreaming: isAssistantStreaming
844
- })
845
- );
846
- });
897
+ dispatch({ type: "advance-reveal", isAssistantStreaming });
847
898
  }, STREAM_REVEAL_TICK_MS);
848
899
  return () => {
849
900
  window.clearInterval(timer);
850
901
  };
851
902
  }, [batchedTargetUnitCount, displayedUnitCount, isAssistantStreaming, message.role]);
852
903
  const settledContent = isFreshBlockActive ? contentBlocks.slice(0, -1).join("\n\n") : displayedContent;
853
- const freshContent = isFreshBlockActive ? contentBlocks.at(-1) ?? "" : "";
904
+ const freshContent = isFreshBlockActive ? contentBlocks[contentBlocks.length - 1] ?? "" : "";
854
905
  return {
855
906
  isAssistantStreaming,
856
907
  displayedContent,
@@ -1808,11 +1859,13 @@ var ChatMessageItemView = ({
1808
1859
  const attachments = message.attachments ?? [];
1809
1860
  const blocks = message.blocks ?? [];
1810
1861
  const hasStructuredBlocks = blocks.length > 0;
1862
+ const hasMarkdownOnlyBlocks = hasStructuredBlocks && blocks.every((block) => block.type === "markdown");
1811
1863
  const hasTextContent = Boolean(settledContent || freshContent || displayedContent);
1864
+ const shouldRenderStructuredBlocks = hasStructuredBlocks && !(isAssistantStreaming && hasMarkdownOnlyBlocks && hasTextContent);
1812
1865
  const isPlanMode = mode === "plan";
1813
1866
  const canSubmitConfirmation = isPlanMode && typeof onConfirmationSubmit === "function";
1814
1867
  const canSubmitQuestionnaire = isPlanMode && typeof onQuestionnaireSubmit === "function";
1815
- const shouldShowStreamingCaret = isAssistantStreaming && (!hasStructuredBlocks || hasTextContent);
1868
+ const shouldShowStreamingCaret = isAssistantStreaming && (!shouldRenderStructuredBlocks || hasTextContent);
1816
1869
  const renderChatMessageBlock = (block, index) => {
1817
1870
  switch (block.type) {
1818
1871
  case "markdown":
@@ -1892,8 +1945,8 @@ var ChatMessageItemView = ({
1892
1945
  isStoppedAssistant ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatusTag, { "data-testid": "chat-message-stopped-tag", children: labels.stoppedResponse }) : null
1893
1946
  ] }),
1894
1947
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Content, { "data-testid": "chat-message-content", children: [
1895
- hasStructuredBlocks || hasTextContent ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(ContentStack, { "data-testid": "chat-message-body-stack", children: [
1896
- hasStructuredBlocks ? blocks.map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1948
+ shouldRenderStructuredBlocks || hasTextContent ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(ContentStack, { "data-testid": "chat-message-body-stack", children: [
1949
+ shouldRenderStructuredBlocks ? blocks.map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1897
1950
  ContentSegment,
1898
1951
  {
1899
1952
  "data-testid": "chat-message-content-segment",
package/dist/index.mjs CHANGED
@@ -590,7 +590,7 @@ var AiChatProvider = (props) => {
590
590
  };
591
591
 
592
592
  // src/components/chat-thread/index.tsx
593
- import { useCallback as useCallback2, useLayoutEffect, useMemo as useMemo3, useRef as useRef4, useState as useState5 } from "react";
593
+ import { useCallback as useCallback2, useLayoutEffect, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
594
594
  import styled10 from "@emotion/styled";
595
595
 
596
596
  // src/context/use-chat-context.ts
@@ -627,7 +627,7 @@ var calculateChatThreadScrollSpacerHeight = ({
627
627
  );
628
628
 
629
629
  // src/components/chat-thread/components/chat-message-item.tsx
630
- import { Fragment, memo, useState as useState4 } from "react";
630
+ import { Fragment, memo, useState as useState3 } from "react";
631
631
  import styled7 from "@emotion/styled";
632
632
  import { keyframes } from "@emotion/react";
633
633
  import ReactMarkdown from "react-markdown";
@@ -636,7 +636,7 @@ import remarkMath from "remark-math";
636
636
  import rehypeKatex from "rehype-katex";
637
637
 
638
638
  // src/components/chat-thread/hooks/use-chat-message-reveal.ts
639
- import { useCallback, useEffect, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
639
+ import { useCallback, useEffect, useMemo as useMemo2, useReducer, useRef as useRef2 } from "react";
640
640
 
641
641
  // src/components/chat-thread/lib/message-reveal.ts
642
642
  var STREAM_REVEAL_TICK_MS = 36;
@@ -683,6 +683,64 @@ var getNextDisplayedUnitCount = ({
683
683
  var splitMarkdownBlocks = (content) => content.split(/\n{2,}/).map((block) => block.trim()).filter(Boolean);
684
684
 
685
685
  // src/components/chat-thread/hooks/use-chat-message-reveal.ts
686
+ var createRevealState = ({
687
+ isAssistantStreaming,
688
+ targetUnitCount
689
+ }) => {
690
+ const initialDisplayedUnitCount = isAssistantStreaming ? 0 : targetUnitCount;
691
+ return {
692
+ batchedTargetUnitCount: initialDisplayedUnitCount,
693
+ displayedUnitCount: initialDisplayedUnitCount,
694
+ isFreshBlockActive: false
695
+ };
696
+ };
697
+ var revealReducer = (state, action) => {
698
+ switch (action.type) {
699
+ case "reset-message":
700
+ return createRevealState(action);
701
+ case "commit-batched-target": {
702
+ const nextDisplayedUnitCount = action.role === "assistant" ? getNextDisplayedUnitCount({
703
+ currentUnits: state.displayedUnitCount,
704
+ targetUnits: action.nextTargetUnitCount,
705
+ isStreaming: action.isAssistantStreaming,
706
+ minimumStep: state.displayedUnitCount > 0 && action.isAssistantStreaming ? 2 : 1
707
+ }) : action.nextTargetUnitCount;
708
+ return {
709
+ ...state,
710
+ batchedTargetUnitCount: action.nextTargetUnitCount,
711
+ displayedUnitCount: nextDisplayedUnitCount
712
+ };
713
+ }
714
+ case "set-fresh-block-active":
715
+ return state.isFreshBlockActive === action.isActive ? state : {
716
+ ...state,
717
+ isFreshBlockActive: action.isActive
718
+ };
719
+ case "sync-displayed-target":
720
+ return state.displayedUnitCount === state.batchedTargetUnitCount ? state : {
721
+ ...state,
722
+ displayedUnitCount: state.batchedTargetUnitCount
723
+ };
724
+ case "advance-reveal": {
725
+ if (state.displayedUnitCount >= state.batchedTargetUnitCount) {
726
+ return state;
727
+ }
728
+ return {
729
+ ...state,
730
+ displayedUnitCount: Math.min(
731
+ state.batchedTargetUnitCount,
732
+ getNextDisplayedUnitCount({
733
+ currentUnits: state.displayedUnitCount,
734
+ targetUnits: state.batchedTargetUnitCount,
735
+ isStreaming: action.isAssistantStreaming
736
+ })
737
+ )
738
+ };
739
+ }
740
+ default:
741
+ return state;
742
+ }
743
+ };
686
744
  var useChatMessageReveal = (message) => {
687
745
  const isAssistantStreaming = message.role === "assistant" && message.status === "streaming";
688
746
  const targetContent = message.content || "";
@@ -690,41 +748,47 @@ var useChatMessageReveal = (message) => {
690
748
  const pendingTargetUnitCountRef = useRef2(targetUnits.length);
691
749
  const batchedTargetUnitCountRef = useRef2(isAssistantStreaming ? 0 : targetUnits.length);
692
750
  const inputBatchTimeoutRef = useRef2(null);
693
- const [batchedTargetUnitCount, setBatchedTargetUnitCount] = useState2(
694
- () => isAssistantStreaming ? 0 : targetUnits.length
695
- );
696
751
  const lastDisplayedBlockCountRef = useRef2(0);
697
- const [displayedUnitCount, setDisplayedUnitCount] = useState2(
698
- () => isAssistantStreaming ? 0 : targetUnits.length
752
+ const previousMessageIdRef = useRef2(message.id);
753
+ const [state, dispatch] = useReducer(
754
+ revealReducer,
755
+ {
756
+ isAssistantStreaming,
757
+ targetUnitCount: targetUnits.length
758
+ },
759
+ createRevealState
699
760
  );
700
- const [isFreshBlockActive, setIsFreshBlockActive] = useState2(false);
761
+ const { batchedTargetUnitCount, displayedUnitCount, isFreshBlockActive } = state;
701
762
  const commitBatchedTargetUnitCount = useCallback(
702
763
  (nextTargetUnitCount) => {
703
764
  batchedTargetUnitCountRef.current = nextTargetUnitCount;
704
- setBatchedTargetUnitCount(nextTargetUnitCount);
705
- setDisplayedUnitCount(
706
- (current) => message.role === "assistant" ? getNextDisplayedUnitCount({
707
- currentUnits: current,
708
- targetUnits: nextTargetUnitCount,
709
- isStreaming: isAssistantStreaming,
710
- minimumStep: current > 0 && isAssistantStreaming ? 2 : 1
711
- }) : nextTargetUnitCount
712
- );
765
+ dispatch({
766
+ type: "commit-batched-target",
767
+ isAssistantStreaming,
768
+ nextTargetUnitCount,
769
+ role: message.role
770
+ });
713
771
  },
714
772
  [isAssistantStreaming, message.role]
715
773
  );
716
774
  useEffect(() => {
775
+ if (previousMessageIdRef.current === message.id) {
776
+ return;
777
+ }
778
+ previousMessageIdRef.current = message.id;
717
779
  pendingTargetUnitCountRef.current = targetUnits.length;
718
780
  batchedTargetUnitCountRef.current = isAssistantStreaming ? 0 : targetUnits.length;
719
- setBatchedTargetUnitCount(batchedTargetUnitCountRef.current);
720
- setDisplayedUnitCount(isAssistantStreaming ? 0 : targetUnits.length);
721
781
  lastDisplayedBlockCountRef.current = 0;
722
782
  if (inputBatchTimeoutRef.current !== null) {
723
783
  window.clearTimeout(inputBatchTimeoutRef.current);
724
784
  inputBatchTimeoutRef.current = null;
725
785
  }
726
- setIsFreshBlockActive(false);
727
- }, [message.id, isAssistantStreaming, targetUnits.length]);
786
+ dispatch({
787
+ type: "reset-message",
788
+ isAssistantStreaming,
789
+ targetUnitCount: targetUnits.length
790
+ });
791
+ }, [isAssistantStreaming, message.id, targetUnits.length]);
728
792
  useEffect(() => {
729
793
  pendingTargetUnitCountRef.current = targetUnits.length;
730
794
  if (message.role !== "assistant" || !isAssistantStreaming) {
@@ -767,9 +831,9 @@ var useChatMessageReveal = (message) => {
767
831
  if (!hasNewDisplayedBlock) {
768
832
  return;
769
833
  }
770
- setIsFreshBlockActive(true);
834
+ dispatch({ type: "set-fresh-block-active", isActive: true });
771
835
  const timer = window.setTimeout(() => {
772
- setIsFreshBlockActive(false);
836
+ dispatch({ type: "set-fresh-block-active", isActive: false });
773
837
  }, STREAM_FRESH_BLOCK_SETTLE_MS);
774
838
  return () => {
775
839
  window.clearTimeout(timer);
@@ -779,32 +843,19 @@ var useChatMessageReveal = (message) => {
779
843
  const shouldAnimateReveal = message.role === "assistant" && displayedUnitCount < batchedTargetUnitCount && (isAssistantStreaming || displayedUnitCount > 0);
780
844
  if (!shouldAnimateReveal) {
781
845
  if (displayedUnitCount !== batchedTargetUnitCount) {
782
- setDisplayedUnitCount(batchedTargetUnitCount);
846
+ dispatch({ type: "sync-displayed-target" });
783
847
  }
784
848
  return;
785
849
  }
786
850
  const timer = window.setInterval(() => {
787
- setDisplayedUnitCount((current) => {
788
- if (current >= batchedTargetUnitCount) {
789
- window.clearInterval(timer);
790
- return current;
791
- }
792
- return Math.min(
793
- batchedTargetUnitCount,
794
- getNextDisplayedUnitCount({
795
- currentUnits: current,
796
- targetUnits: batchedTargetUnitCount,
797
- isStreaming: isAssistantStreaming
798
- })
799
- );
800
- });
851
+ dispatch({ type: "advance-reveal", isAssistantStreaming });
801
852
  }, STREAM_REVEAL_TICK_MS);
802
853
  return () => {
803
854
  window.clearInterval(timer);
804
855
  };
805
856
  }, [batchedTargetUnitCount, displayedUnitCount, isAssistantStreaming, message.role]);
806
857
  const settledContent = isFreshBlockActive ? contentBlocks.slice(0, -1).join("\n\n") : displayedContent;
807
- const freshContent = isFreshBlockActive ? contentBlocks.at(-1) ?? "" : "";
858
+ const freshContent = isFreshBlockActive ? contentBlocks[contentBlocks.length - 1] ?? "" : "";
808
859
  return {
809
860
  isAssistantStreaming,
810
861
  displayedContent,
@@ -1023,7 +1074,7 @@ var Value = styled3.span`
1023
1074
  `;
1024
1075
 
1025
1076
  // src/components/chat-thread/components/pde-ai-questionnaire-card.tsx
1026
- import { useState as useState3 } from "react";
1077
+ import { useState as useState2 } from "react";
1027
1078
  import styled4 from "@emotion/styled";
1028
1079
  import { jsx as jsx5, jsxs as jsxs3 } from "@emotion/react/jsx-runtime";
1029
1080
  var OTHER_OPTION_VALUE = "__other__";
@@ -1125,10 +1176,10 @@ var PDEAIQuestionnaireCardInner = ({
1125
1176
  interactive = false,
1126
1177
  onSubmit
1127
1178
  }) => {
1128
- const [answers, setAnswers] = useState3(
1179
+ const [answers, setAnswers] = useState2(
1129
1180
  () => createInitialAnswers(questionnaire)
1130
1181
  );
1131
- const [errorMessage, setErrorMessage] = useState3(null);
1182
+ const [errorMessage, setErrorMessage] = useState2(null);
1132
1183
  const handleSubmit = () => {
1133
1184
  const missingQuestions = questionnaire.questions.filter(
1134
1185
  (question) => question.required && isMissingRequiredAnswer(question, answers)
@@ -1756,17 +1807,19 @@ var ChatMessageItemView = ({
1756
1807
  renderMessageBlock
1757
1808
  }) => {
1758
1809
  const { labels } = useChatContext();
1759
- const [activeImage, setActiveImage] = useState4(void 0);
1810
+ const [activeImage, setActiveImage] = useState3(void 0);
1760
1811
  const { displayedContent, freshContent, isAssistantStreaming, settledContent } = useChatMessageReveal(message);
1761
1812
  const isStoppedAssistant = message.role === "assistant" && message.status === "stopped";
1762
1813
  const attachments = message.attachments ?? [];
1763
1814
  const blocks = message.blocks ?? [];
1764
1815
  const hasStructuredBlocks = blocks.length > 0;
1816
+ const hasMarkdownOnlyBlocks = hasStructuredBlocks && blocks.every((block) => block.type === "markdown");
1765
1817
  const hasTextContent = Boolean(settledContent || freshContent || displayedContent);
1818
+ const shouldRenderStructuredBlocks = hasStructuredBlocks && !(isAssistantStreaming && hasMarkdownOnlyBlocks && hasTextContent);
1766
1819
  const isPlanMode = mode === "plan";
1767
1820
  const canSubmitConfirmation = isPlanMode && typeof onConfirmationSubmit === "function";
1768
1821
  const canSubmitQuestionnaire = isPlanMode && typeof onQuestionnaireSubmit === "function";
1769
- const shouldShowStreamingCaret = isAssistantStreaming && (!hasStructuredBlocks || hasTextContent);
1822
+ const shouldShowStreamingCaret = isAssistantStreaming && (!shouldRenderStructuredBlocks || hasTextContent);
1770
1823
  const renderChatMessageBlock = (block, index) => {
1771
1824
  switch (block.type) {
1772
1825
  case "markdown":
@@ -1846,8 +1899,8 @@ var ChatMessageItemView = ({
1846
1899
  isStoppedAssistant ? /* @__PURE__ */ jsx8(StatusTag, { "data-testid": "chat-message-stopped-tag", children: labels.stoppedResponse }) : null
1847
1900
  ] }),
1848
1901
  /* @__PURE__ */ jsxs5(Content, { "data-testid": "chat-message-content", children: [
1849
- hasStructuredBlocks || hasTextContent ? /* @__PURE__ */ jsxs5(ContentStack, { "data-testid": "chat-message-body-stack", children: [
1850
- hasStructuredBlocks ? blocks.map((block, index) => /* @__PURE__ */ jsx8(
1902
+ shouldRenderStructuredBlocks || hasTextContent ? /* @__PURE__ */ jsxs5(ContentStack, { "data-testid": "chat-message-body-stack", children: [
1903
+ shouldRenderStructuredBlocks ? blocks.map((block, index) => /* @__PURE__ */ jsx8(
1851
1904
  ContentSegment,
1852
1905
  {
1853
1906
  "data-testid": "chat-message-content-segment",
@@ -2233,7 +2286,7 @@ var ChatThreadView = ({
2233
2286
  const latestUserMessageRef = useRef4(null);
2234
2287
  const pendingScrollUserMessageIdRef = useRef4(void 0);
2235
2288
  const reservedSpaceFrameRef = useRef4(null);
2236
- const [latestUserMessageReservedSpace, setLatestUserMessageReservedSpace] = useState5({ messageId: void 0, value: 0 });
2289
+ const [latestUserMessageReservedSpace, setLatestUserMessageReservedSpace] = useState4({ messageId: void 0, value: 0 });
2237
2290
  const reservedPaddingBottom = 24 + (latestUserMessageReservedSpace.messageId === latestUserMessageId ? latestUserMessageReservedSpace.value : 0);
2238
2291
  const measureLatestUserMessageReservedSpace = useCallback2((messageId) => {
2239
2292
  const container = containerRef.current;
@@ -2559,10 +2612,10 @@ var resolveSendSession = ({
2559
2612
  };
2560
2613
 
2561
2614
  // src/components/chat-composer/hooks/use-chat-composer.ts
2562
- import { useCallback as useCallback3, useEffect as useEffect4, useRef as useRef6, useState as useState7 } from "react";
2615
+ import { useCallback as useCallback3, useEffect as useEffect4, useRef as useRef6, useState as useState6 } from "react";
2563
2616
 
2564
2617
  // src/components/chat-composer/hooks/use-composer-attachments.ts
2565
- import { useEffect as useEffect3, useRef as useRef5, useState as useState6 } from "react";
2618
+ import { useEffect as useEffect3, useRef as useRef5, useState as useState5 } from "react";
2566
2619
  var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp"]);
2567
2620
  var MAX_COMPOSER_ATTACHMENTS = 10;
2568
2621
  var createObjectUrl = (file) => typeof URL !== "undefined" && typeof URL.createObjectURL === "function" ? URL.createObjectURL(file) : "";
@@ -2576,7 +2629,7 @@ var releaseComposerAttachments = (attachments) => {
2576
2629
  attachments.forEach((attachment) => revokeObjectUrl(attachment.previewUrl));
2577
2630
  };
2578
2631
  var useComposerAttachments = () => {
2579
- const [attachments, setAttachments] = useState6([]);
2632
+ const [attachments, setAttachments] = useState5([]);
2580
2633
  const attachmentsRef = useRef5([]);
2581
2634
  useEffect3(() => {
2582
2635
  attachmentsRef.current = attachments;
@@ -2694,9 +2747,9 @@ var useChatComposer = () => {
2694
2747
  const clearSessionError = useChatStore((s) => s.clearSessionError);
2695
2748
  const setPreferredMode = useChatStore((s) => s.setPreferredMode);
2696
2749
  const setSessionMode = useChatStore((s) => s.setSessionMode);
2697
- const [availableModels, setAvailableModels] = useState7([]);
2698
- const [isModelsLoading, setIsModelsLoading] = useState7(true);
2699
- const [isModelsError, setIsModelsError] = useState7(false);
2750
+ const [availableModels, setAvailableModels] = useState6([]);
2751
+ const [isModelsLoading, setIsModelsLoading] = useState6(true);
2752
+ const [isModelsError, setIsModelsError] = useState6(false);
2700
2753
  const fetchModels = useCallback3(async () => {
2701
2754
  setIsModelsLoading(true);
2702
2755
  setIsModelsError(false);
@@ -2713,10 +2766,10 @@ var useChatComposer = () => {
2713
2766
  void fetchModels();
2714
2767
  }, [fetchModels]);
2715
2768
  const hasModels = availableModels.length > 0;
2716
- const [value, setValue] = useState7("");
2717
- const [selectedModel, setSelectedModel] = useState7("");
2718
- const [selectedMode, setSelectedModeLocal] = useState7(DEFAULT_CHAT_AGENT_MODE);
2719
- const [attachmentNotice, setAttachmentNotice] = useState7(null);
2769
+ const [value, setValue] = useState6("");
2770
+ const [selectedModel, setSelectedModel] = useState6("");
2771
+ const [selectedMode, setSelectedModeLocal] = useState6(DEFAULT_CHAT_AGENT_MODE);
2772
+ const [attachmentNotice, setAttachmentNotice] = useState6(null);
2720
2773
  const { attachments, appendFiles, removeAttachment, takeMessageAttachments } = useComposerAttachments();
2721
2774
  const abortControllerRef = useRef6(null);
2722
2775
  const stopRequestRef = useRef6(null);
@@ -3012,14 +3065,14 @@ var useChatComposer = () => {
3012
3065
  };
3013
3066
 
3014
3067
  // src/components/chat-composer/components/chat-composer-attachment-list.tsx
3015
- import { useState as useState8 } from "react";
3068
+ import { useState as useState7 } from "react";
3016
3069
  import styled11 from "@emotion/styled";
3017
3070
  import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs8 } from "@emotion/react/jsx-runtime";
3018
3071
  var ChatComposerAttachmentList = ({
3019
3072
  attachments,
3020
3073
  onRemoveAttachment
3021
3074
  }) => {
3022
- const [activeImage, setActiveImage] = useState8(null);
3075
+ const [activeImage, setActiveImage] = useState7(null);
3023
3076
  if (!attachments.length) {
3024
3077
  return null;
3025
3078
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xinghunm/ai-chat",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "AI chat React component library",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",