@amaster.ai/components-templates 1.4.0 → 1.4.2

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.
@@ -0,0 +1,16 @@
1
+ ## tailwind.config.js
2
+
3
+ content 添加 "./node_modules/@json-render/shadcn/dist/**/*.js"
4
+ ```
5
+ {
6
+ content: [
7
+ "./node_modules/@json-render/shadcn/dist/**/*.js"
8
+ ]
9
+ }
10
+ ```
11
+
12
+ ## src/lib/client 保证是新的
13
+ ```
14
+ import { createClient } from "@amaster.ai/client";
15
+ export const client = createClient({});
16
+ ```
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-assistant",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "AI Assistant component template",
5
5
  "main": "template/index.ts",
6
6
  "template": {
@@ -29,6 +29,9 @@
29
29
  "@amaster.ai/client": "1.1.0-beta.52",
30
30
  "react-markdown": "^10.0.0",
31
31
  "remark-gfm": "^4.0.0",
32
- "immer": "^10.0.0"
32
+ "immer": "^10.0.0",
33
+ "@json-render/core": "^0.12.0",
34
+ "@json-render/react": "^0.12.0",
35
+ "@json-render/shadcn": "^0.12.0"
33
36
  }
34
37
  }
@@ -10,8 +10,10 @@ import ChatDisplayModeSwitcher from "./components/chat-display-mode-switcher";
10
10
  import { useDisplayMode } from "./hooks/useDisplayMode";
11
11
  import InlineAIAssistant from "./inline-ai-assistant";
12
12
 
13
- const AIAssistant: React.FC = () => {
14
-
13
+ const AIAssistant: React.FC<{
14
+ greeting?: string;
15
+ recommends?: string[];
16
+ }> = ({ greeting, recommends }) => {
15
17
  const [isOpen, setIsOpen] = useState(false);
16
18
  const [displayMode, setDisplayMode] = useDisplayMode(isOpen);
17
19
  const isFullscreen = displayMode === "fullscreen" && isOpen;
@@ -67,6 +69,7 @@ const AIAssistant: React.FC = () => {
67
69
  <ChatHeader
68
70
  disabledDrag={displayMode !== "floating"}
69
71
  isDragging={isDragging}
72
+ displayMode={displayMode}
70
73
  onMouseDown={handleDragStart}
71
74
  onTouchStart={handleTouchStart}
72
75
  onClose={handleClose}
@@ -77,7 +80,13 @@ const AIAssistant: React.FC = () => {
77
80
  />
78
81
  </ChatHeader>
79
82
 
80
- <InlineAIAssistant showBanner={false} className="flex-1" greeting={getText().greeting} />
83
+ <InlineAIAssistant
84
+ showBanner={false}
85
+ className="flex-1"
86
+ greeting={greeting || getText().greeting}
87
+ recommends={recommends}
88
+ displayMode={displayMode}
89
+ />
81
90
  </ChatFloatingCard>
82
91
  )}
83
92
  </>
@@ -116,7 +116,6 @@ const ChatToolMessage: React.FC<
116
116
  <span className="flex-1 truncate flex-shrink-0">
117
117
  {message.toolName || getText().unknownTool}
118
118
  </span>
119
- {/* {message.toolDescription && <div className="flex-1 truncate max-w-1/2">{message.toolDescription}</div>} */}
120
119
  </div>
121
120
  );
122
121
  };
@@ -129,11 +128,25 @@ const ChatErrorMessage: React.FC<{ message: TextMessage }> = ({ message }) => {
129
128
  );
130
129
  };
131
130
 
131
+ export const ChatDivider = () => {
132
+ return (
133
+ <div className="flex items-center justify-center gap-4 w-full my-4">
134
+ <div className="h-px bg-border flex-1" />
135
+ <span className="text-xs text-muted-foreground">
136
+ {getText().newConversation}
137
+ </span>
138
+ <div className="h-px bg-border flex-1" />
139
+ </div>
140
+ );
141
+ };
142
+
132
143
  const ChatUIRenderMessage: React.FC<{ message: UIRenderMessage }> = ({
133
144
  message,
134
145
  }) => {
135
146
  return (
136
- <UIRenderer spec={message.spec} className="mb-2" />
147
+ <div className="flex-1 overflow-hidden">
148
+ <UIRenderer spec={message.spec} className="mb-2" />
149
+ </div>
137
150
  );
138
151
  };
139
152
 
@@ -154,7 +167,9 @@ const MessageContentRenderer: React.FC<
154
167
  case "error":
155
168
  return <ChatErrorMessage message={message as TextMessage} {...rest} />;
156
169
  case "ui-render":
157
- return <ChatUIRenderMessage message={message as UIRenderMessage} />;
170
+ return (
171
+ <ChatUIRenderMessage message={message as UIRenderMessage} {...rest} />
172
+ );
158
173
  default:
159
174
  return <div>{getText().unknownTool}</div>;
160
175
  }
@@ -166,15 +181,17 @@ const ChatAssistantMessage: React.FC<
166
181
  showAvatar?: boolean;
167
182
  } & MessageCommonProps
168
183
  > = ({ message, showAvatar, ...rest }) => {
184
+
169
185
  return (
170
186
  <div
187
+ data-conversation-message-id={(message as any).messageId}
171
188
  className={cn(
172
189
  "flex gap-3 animate-in fade-in-0 slide-in-from-bottom-2 duration-300 overflow-hidden w-full chat-assistant-message &+.chat-assistant-message:mt-4 group",
173
190
  )}
174
191
  >
175
192
  <div
176
193
  className={cn(
177
- "flex-shrink-0 h-7 w-7 rounded-full bg-gradient-to-br from-[#6366F1] to-[#8B5CF6] flex items-center justify-center text-left",
194
+ "flex-shrink-0 h-7 w-7 rounded-full bg-gradient-to-br from-primary to-primary/40 flex items-center justify-center text-left",
178
195
  !showAvatar && "invisible",
179
196
  )}
180
197
  >
@@ -3,12 +3,7 @@ import { getText } from "../i18n";
3
3
  import { HoverCard } from "@radix-ui/react-hover-card";
4
4
  import { HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card";
5
5
  import { Button } from "@/components/ui/button";
6
-
7
- export type IDisplayMode =
8
- | "fullscreen"
9
- | "floating"
10
- | "side-left"
11
- | "side-right";
6
+ import type { IDisplayMode } from "../types";
12
7
 
13
8
  const ChatDisplayModeSwitcher: React.FC<{
14
9
  displayMode: IDisplayMode;
@@ -31,7 +31,7 @@ const ChatFloatingButton: React.FC<ChatFloatingButtonProps> = ({
31
31
  size="lg"
32
32
  className={`
33
33
  group relative h-full w-full rounded-full
34
- bg-gradient-to-br from-[#6366F1] to-[#8B5CF6]
34
+ bg-gradient-to-br from-primary to-primary/40
35
35
  text-white
36
36
  border-0
37
37
  transition-all duration-300 ease-out select-none
@@ -43,7 +43,7 @@ const ChatFloatingButton: React.FC<ChatFloatingButtonProps> = ({
43
43
  }
44
44
  `}
45
45
  >
46
- <span className="absolute inset-0 rounded-full bg-[#6366F1] animate-ping opacity-20" />
46
+ <span className="absolute inset-0 rounded-full bg-primary animate-ping opacity-20" />
47
47
  <MessageSquare
48
48
  className="relative h-5 w-5 pointer-events-none"
49
49
  strokeWidth={1.75}
@@ -2,7 +2,7 @@ import type React from "react";
2
2
  import { useRef } from "react";
3
3
  import { Card } from "@/components/ui/card";
4
4
  import { cn } from "@/lib/utils";
5
- import { IDisplayMode } from "./chat-display-mode-switcher";
5
+ import { IDisplayMode } from "../types";
6
6
 
7
7
  interface ChatFloatingCardProps {
8
8
  displayMode: IDisplayMode;
@@ -2,8 +2,11 @@ import { MessageSquare, X } from "lucide-react";
2
2
  import type React from "react";
3
3
  import { Button } from "@/components/ui/button";
4
4
  import { getText } from "../i18n";
5
+ import { cn } from "@/lib/utils";
6
+ import { IDisplayMode } from "../types";
5
7
 
6
8
  interface ChatHeaderProps {
9
+ displayMode?: IDisplayMode;
7
10
  disabledDrag?: boolean;
8
11
  isDragging?: boolean;
9
12
  onMouseDown?: (e: React.MouseEvent) => void;
@@ -13,6 +16,7 @@ interface ChatHeaderProps {
13
16
  }
14
17
 
15
18
  const ChatHeader: React.FC<ChatHeaderProps> = ({
19
+ displayMode,
16
20
  disabledDrag,
17
21
  isDragging,
18
22
  onMouseDown,
@@ -22,27 +26,25 @@ const ChatHeader: React.FC<ChatHeaderProps> = ({
22
26
  }) => {
23
27
  return (
24
28
  <div
25
- className={`
26
- flex items-center justify-between px-4 py-3
27
- border-b border-[#E5E7EB]
28
- bg-white
29
- ${!disabledDrag && onMouseDown ? (isDragging ? "cursor-grabbing" : "cursor-grab") : ""}
30
- `}
29
+ className={cn(
30
+ "flex items-center justify-between px-4",
31
+ !disabledDrag && onMouseDown
32
+ ? isDragging
33
+ ? "cursor-grabbing"
34
+ : "cursor-grab"
35
+ : "",
36
+ {
37
+ 'border-b border-[#E5E7EB] bg-white': displayMode === "floating",
38
+ },
39
+ )}
31
40
  onMouseDown={!disabledDrag ? onMouseDown : undefined}
32
41
  onTouchStart={!disabledDrag ? onTouchStart : undefined}
33
42
  >
34
43
  <div className="flex items-center gap-2.5">
35
- <div className="flex items-center justify-center h-8 w-8 rounded-full bg-gradient-to-br from-[#6366F1] to-[#8B5CF6]">
36
- <MessageSquare className="h-4 w-4 text-white" strokeWidth={2} />
37
- </div>
38
44
  <div className="flex flex-col">
39
45
  <span className="text-sm font-semibold text-[#111827]">
40
- AI Assistant
46
+ {getText().assistantName}
41
47
  </span>
42
- <div className="flex items-center gap-1.5">
43
- <span className="h-1.5 w-1.5 rounded-full bg-[#10B981] animate-pulse" />
44
- <span className="text-xs text-[#6B7280]">{getText().online}</span>
45
- </div>
46
48
  </div>
47
49
  </div>
48
50
  <div className="flex items-center gap-1">
@@ -1,9 +1,15 @@
1
- import { ArrowUp, MessageCirclePlus } from "lucide-react";
1
+ import {
2
+ ArrowUp,
3
+ LoaderCircle,
4
+ MessageCirclePlus,
5
+ RotateCw,
6
+ Square,
7
+ } from "lucide-react";
2
8
  import type React from "react";
3
9
  import { Button } from "@/components/ui/button";
4
10
  import { getText } from "../i18n";
5
- import type { Conversation } from "../types";
6
- import { useState } from "react";
11
+ import type { Conversation, IDisplayMode } from "../types";
12
+ import { useMemo, useState } from "react";
7
13
  import { cn } from "@/lib/utils";
8
14
  import {
9
15
  Tooltip,
@@ -13,18 +19,19 @@ import {
13
19
  } from "@/components/ui/tooltip";
14
20
  import VoiceInputButton from "./voice-input";
15
21
 
16
- const ResetButton: React.FC<{ onReset?: () => void }> = ({ onReset }) => {
17
- if (!onReset) return null;
22
+ const NewConvButton: React.FC<{ onNew?: () => void; disabled?: boolean }> = ({ onNew, disabled }) => {
23
+ if (!onNew) return null;
18
24
  return (
19
25
  <TooltipProvider>
20
26
  <Tooltip delayDuration={0}>
21
- <TooltipContent>{getText().resetConversation}</TooltipContent>
27
+ <TooltipContent>{getText().newConversation}</TooltipContent>
22
28
  <TooltipTrigger asChild>
23
29
  <Button
24
30
  type="button"
25
31
  variant="ghost"
26
32
  size="icon"
27
- onClick={onReset}
33
+ onClick={onNew}
34
+ disabled={disabled}
28
35
  className="h-8 w-8 text-[#6B7280] hover:text-[#374151] hover:bg-[#F3F4F6] rounded-lg transition-colors duration-200 cursor-pointer"
29
36
  >
30
37
  <MessageCirclePlus className="size-5" />
@@ -35,29 +42,56 @@ const ResetButton: React.FC<{ onReset?: () => void }> = ({ onReset }) => {
35
42
  );
36
43
  };
37
44
 
38
- const SubmitButton: React.FC<{ disabled: boolean; onClick: () => void }> = ({
39
- disabled,
45
+ const SubmitButton: React.FC<{
46
+ disabled: boolean;
47
+ starting?: boolean;
48
+ onClick: () => void;
49
+ }> = ({ disabled, starting, onClick }) => {
50
+ return (
51
+ <Button
52
+ type="button"
53
+ onClick={onClick}
54
+ disabled={disabled || starting}
55
+ size="icon"
56
+ className={cn(
57
+ "size-8 text-white rounded-xl transition-all duration-200 cursor-pointer shadow-[0_2px_8px_rgba(99,102,241,0.3)] hover:shadow-[0_4px_12px_rgba(99,102,241,0.4)]",
58
+ "bg-gradient-to-r from-primary to-primary/80",
59
+ "hover:from-primary/90 hover:to-primary/70",
60
+ "disabled:opacity-40 disabled:cursor-not-allowed",
61
+ starting && "bg-gray-600",
62
+ )}
63
+ >
64
+ {starting ? (
65
+ <LoaderCircle className="h-4 w-4 animate-spin" strokeWidth={2} />
66
+ ) : (
67
+ <ArrowUp className="h-4 w-4" strokeWidth={2} />
68
+ )}
69
+ </Button>
70
+ );
71
+ };
72
+
73
+ const StopButton: React.FC<{ onClick: () => void; disabled?: boolean }> = ({
40
74
  onClick,
75
+ disabled,
41
76
  }) => {
42
77
  return (
43
78
  <Button
44
79
  type="button"
45
80
  onClick={onClick}
46
- disabled={disabled}
47
81
  size="icon"
82
+ disabled={disabled}
48
83
  className="
49
84
  size-8
50
- bg-gradient-to-r from-[#6366F1] to-[#8B5CF6]
51
- hover:from-[#4F46E5] hover:to-[#7C3AED]
85
+ bg-foreground
86
+ hover:bg-foreground/50
52
87
  text-white
53
88
  rounded-xl
54
89
  transition-all duration-200
55
90
  cursor-pointer
56
- disabled:opacity-40 disabled:cursor-not-allowed
57
- shadow-[0_2px_8px_rgba(99,102,241,0.3)]
58
- hover:shadow-[0_4px_12px_rgba(99,102,241,0.4)]"
91
+ shadow-[0_2px_8px_rgba(239,68,68,0.3)]
92
+ hover:shadow-[0_4px_12px_rgba(239,68,68,0.4)]"
59
93
  >
60
- <ArrowUp className="h-4 w-4" strokeWidth={2} />
94
+ <Square className="h-3 w-3 fill-current" strokeWidth={2} />
61
95
  </Button>
62
96
  );
63
97
  };
@@ -68,7 +102,10 @@ interface ChatInputProps {
68
102
  inputValue: string;
69
103
  onInputChange: (value: string) => void;
70
104
  onSendMessage: () => void;
71
- onReset?: () => void;
105
+ onNew?: () => void;
106
+ onCancel?: () => void;
107
+ starting?: boolean;
108
+ displayMode?: IDisplayMode;
72
109
  }
73
110
 
74
111
  const ChatInput: React.FC<ChatInputProps> = ({
@@ -77,9 +114,19 @@ const ChatInput: React.FC<ChatInputProps> = ({
77
114
  inputValue,
78
115
  onInputChange,
79
116
  onSendMessage,
80
- onReset,
117
+ onNew,
118
+ onCancel,
119
+ starting,
120
+ displayMode
81
121
  }) => {
82
- const hasConversations = conversations.length > 1;
122
+ const hasConversations = conversations.length > 0;
123
+ const lastConv =
124
+ conversations.length > 0 ? conversations[conversations.length - 1] : null;
125
+ const lastIsDivider = useMemo(() => {
126
+ if (!lastConv) return false;
127
+ return (lastConv.system?.level === "newConversation");
128
+ }, [lastConv]);
129
+
83
130
  const [focus, setFocus] = useState(false);
84
131
  const handleKeyPress = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
85
132
  if (e.key === "Enter" && !e.shiftKey) {
@@ -92,9 +139,9 @@ const ChatInput: React.FC<ChatInputProps> = ({
92
139
  <div className="py-3 px-4">
93
140
  <div
94
141
  className={cn(
95
- "w-full rounded-xl bg-white transition-all duration-200 p-3 border border-[#6366F1]/80",
142
+ "w-full rounded-xl bg-white transition-all duration-200 p-3 border border-primary/80",
96
143
  {
97
- "border-[#6366F1] ring-2 ring-[#6366F1]/20": focus
144
+ "border-primary ring-2 ring-primary/20": focus,
98
145
  },
99
146
  )}
100
147
  >
@@ -112,7 +159,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
112
159
  />
113
160
  <div className="flex items-center justify-between gap-2 mt-1">
114
161
  <div className="flex items-center gap-1">
115
- <ResetButton onReset={hasConversations ? onReset : undefined} />
162
+ {onNew && <NewConvButton onNew={onNew} disabled={!hasConversations || lastIsDivider || starting || isLoading} />}
116
163
  </div>
117
164
  <div className="flex items-center gap-2">
118
165
  <VoiceInputButton
@@ -121,17 +168,22 @@ const ChatInput: React.FC<ChatInputProps> = ({
121
168
  setFocus(true);
122
169
  }}
123
170
  value={inputValue}
124
- disabled={isLoading}
125
- />
126
- <SubmitButton
127
- onClick={() => onSendMessage()}
128
- disabled={!inputValue.trim() || isLoading}
171
+ disabled={isLoading || starting}
129
172
  />
173
+ {isLoading ? (
174
+ <StopButton onClick={onCancel || (() => {})} />
175
+ ) : (
176
+ <SubmitButton
177
+ onClick={() => onSendMessage()}
178
+ disabled={!inputValue.trim() || isLoading}
179
+ starting={starting}
180
+ />
181
+ )}
130
182
  </div>
131
183
  </div>
132
184
  </div>
133
185
 
134
- {conversations.length > 0 && (
186
+ {(conversations.length > 0 || displayMode !== "inline") && (
135
187
  <p className="text-[10px] text-[#9CA3AF] mt-2 text-center">
136
188
  {getText().footerAiWarning}
137
189
  </p>
@@ -1,36 +1,106 @@
1
1
  import type React from "react";
2
+ import { useEffect, useRef, useCallback } from "react";
2
3
  import type { Conversation } from "../types";
3
- import ChatAssistantMessage from "./chat-assistant-message";
4
+ import ChatAssistantMessage, { ChatDivider } from "./chat-assistant-message";
4
5
  import ChatUserMessage from "./chat-user-message";
5
6
  import { cn } from "@/lib/utils";
7
+ import { LoaderCircle } from "lucide-react";
8
+ import { getText } from "../i18n";
6
9
 
7
10
  interface ChatMessagesProps {
8
11
  conversations: Conversation[];
9
12
  isLoading: boolean;
13
+ isLoadingHistory?: boolean;
14
+ hasMoreHistory?: boolean;
10
15
  scrollAreaRef: React.RefObject<HTMLDivElement>;
11
16
  messagesEndRef: React.RefObject<HTMLDivElement>;
12
17
  className?: string;
18
+ onLoadMore?: () => void;
13
19
  }
14
20
 
15
21
  const ChatMessages: React.FC<ChatMessagesProps> = ({
16
22
  conversations,
17
23
  isLoading,
24
+ isLoadingHistory,
25
+ hasMoreHistory,
18
26
  scrollAreaRef,
19
27
  messagesEndRef,
20
- className
28
+ className,
29
+ onLoadMore,
21
30
  }) => {
31
+ const isNearTopRef = useRef(false);
32
+ const lastScrollTopRef = useRef(0);
33
+
34
+ const handleScroll = useCallback(() => {
35
+ const scrollArea = scrollAreaRef.current;
36
+ if (!scrollArea || !onLoadMore || !hasMoreHistory || isLoadingHistory)
37
+ return;
38
+
39
+ const scrollTop = scrollArea.scrollTop;
40
+
41
+ const isAtTop = scrollTop < 50;
42
+ const wasNearTop = isNearTopRef.current;
43
+
44
+ if (isAtTop && !wasNearTop && scrollTop < lastScrollTopRef.current) {
45
+ onLoadMore();
46
+ }
47
+
48
+ isNearTopRef.current = isAtTop;
49
+ lastScrollTopRef.current = scrollTop;
50
+ }, [scrollAreaRef, onLoadMore, hasMoreHistory, isLoadingHistory]);
51
+
52
+ useEffect(() => {
53
+ const scrollArea = scrollAreaRef.current;
54
+ if (!scrollArea) return;
55
+
56
+ scrollArea.addEventListener("scroll", handleScroll, { passive: true });
57
+ return () => {
58
+ scrollArea.removeEventListener("scroll", handleScroll);
59
+ };
60
+ }, [handleScroll, scrollAreaRef]);
61
+
62
+ const convLength = conversations.length;
63
+
22
64
  return (
23
65
  <div
24
66
  ref={scrollAreaRef}
25
- className={cn('text-sm flex flex-col gap-4 w-full py-4 pb-0 min-h-0 overflow-x-hidden overflow-auto max-w-full min-w-0 px-4', {
26
- 'flex-1': conversations.length > 0,
27
- }, className)}
67
+ className={cn(
68
+ "text-sm flex flex-col gap-4 w-full py-4 pb-0 min-h-0 overflow-x-hidden overflow-auto max-w-full min-w-0 px-4",
69
+ {
70
+ "flex-1": convLength > 0,
71
+ },
72
+ className,
73
+ )}
28
74
  data-role="chat-messages"
29
75
  >
76
+ {isLoadingHistory ? (
77
+ <div
78
+ key="loading-history"
79
+ className="flex justify-center items-center gap-2 text-center"
80
+ >
81
+ <LoaderCircle className="size-4 animate-spin" />
82
+ <span>{getText().loadingHistory}</span>
83
+ </div>
84
+ ) : hasMoreHistory ? (
85
+ <div className="flex justify-center items-center gap-2 text-center">
86
+ <span>
87
+ {hasMoreHistory ? getText().loadMore : getText().noMoreHistory}
88
+ </span>
89
+ </div>
90
+ ) : null}
91
+
30
92
  {conversations.map((conversation, index) => {
31
93
  const len = conversation.messages.length;
94
+ const historyId = conversation.historyId || "";
95
+ const lastHistoryId = conversations[index - 1]?.historyId || "";
96
+ let addDivider =
97
+ index > 0 && historyId !== lastHistoryId || conversation.system?.level === "newConversation";
98
+
32
99
  return (
33
100
  <div key={conversation.taskId} className="flex flex-col gap-4">
101
+ {addDivider && (
102
+ <ChatDivider key={`${conversation.taskId}-divider`} />
103
+ )}
34
104
  {conversation.messages.map((message, msgIndex) => {
35
105
  const key = message.messageId || `${index}-${msgIndex}`;
36
106
  const isNewest = msgIndex === len - 1;
@@ -4,7 +4,8 @@ const ChatRecommends: React.FC<{
4
4
  hidden?: boolean;
5
5
  data?: string[];
6
6
  onSend: (prompt: string) => void;
7
- }> = ({ hidden, data = getText().defaultRecommendedQuestions, onSend }) => {
7
+ disabled?: boolean;
8
+ }> = ({ hidden, data = getText().defaultRecommendedQuestions, onSend, disabled }) => {
8
9
  if (hidden || !data || data.length === 0) return null;
9
10
  return (
10
11
  <div className="flex flex-wrap gap-2 pt-2 px-4">
@@ -13,6 +14,7 @@ const ChatRecommends: React.FC<{
13
14
  type="button"
14
15
  key={prompt}
15
16
  onClick={() => onSend(prompt)}
17
+ disabled={disabled}
16
18
  className="
17
19
  text-xs px-2 py-1.5
18
20
  bg-[#F3F4F6] hover:bg-[#E5E7EB]
@@ -1,13 +1,24 @@
1
- import { useState } from "react";
2
- import { useConversationProcessor } from "./useConversationProcessor";
1
+ import { useState, useCallback } from "react";
2
+ import { useConversation } from "./useConversation";
3
3
  import { useAutoScroll } from "./useAutoScroll";
4
4
 
5
5
  export const useAssistantStore = () => {
6
6
  const [inputValue, setInputValue] = useState("");
7
7
 
8
- const chatStreamHook = useConversationProcessor();
9
- const { conversations, isLoading, sendMessage, resetConversation } =
10
- chatStreamHook;
8
+ const chatStreamHook = useConversation();
9
+ const {
10
+ conversations,
11
+ isLoading,
12
+ isLoadingHistory,
13
+ historyState,
14
+ sendMessage,
15
+ resetConversation,
16
+ startNewConversation,
17
+ loadHistory,
18
+ loadMoreHistory,
19
+ starting,
20
+ cancelChat,
21
+ } = chatStreamHook;
11
22
 
12
23
  const autoScrollHook = useAutoScroll(conversations, isLoading);
13
24
  const { scrollAreaRef, messagesEndRef, scrollToBottom } = autoScrollHook;
@@ -22,15 +33,33 @@ export const useAssistantStore = () => {
22
33
  await sendMessage(message.trim());
23
34
  };
24
35
 
36
+ const handleNewConversation = useCallback(async () => {
37
+ await startNewConversation();
38
+ }, [startNewConversation]);
39
+
40
+ const handleCancelChat = useCallback(async () => {
41
+ await cancelChat();
42
+ }, [cancelChat]);
43
+
44
+ const handleLoadMore = useCallback(async () => {
45
+ await loadMoreHistory();
46
+ }, [loadMoreHistory]);
47
+
25
48
  return {
26
49
  conversations,
27
50
  isLoading,
51
+ isLoadingHistory,
52
+ historyState,
28
53
  inputValue,
29
54
  setInputValue,
30
55
  sendMessage: handleSendMessage,
31
56
  reset: resetConversation,
57
+ startNewConversation: handleNewConversation,
58
+ cancelChat: handleCancelChat,
59
+ loadMoreHistory: handleLoadMore,
32
60
  scrollAreaRef,
33
61
  messagesEndRef,
34
- scrollToBottom
62
+ scrollToBottom,
63
+ starting,
35
64
  };
36
65
  };