@rimori/react-client 0.4.11-next.2 → 0.4.11-next.3
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.
|
@@ -7,9 +7,6 @@ export interface BuddyAssistantAutoStart {
|
|
|
7
7
|
userMessage?: string;
|
|
8
8
|
}
|
|
9
9
|
export interface BuddyAssistantProps {
|
|
10
|
-
buddyName: string;
|
|
11
|
-
avatarImageUrl: string;
|
|
12
|
-
voiceId: string;
|
|
13
10
|
systemPrompt: string;
|
|
14
11
|
autoStartConversation?: BuddyAssistantAutoStart;
|
|
15
12
|
circleSize?: string;
|
|
@@ -19,4 +16,4 @@ export interface BuddyAssistantProps {
|
|
|
19
16
|
voiceSpeed?: number;
|
|
20
17
|
tools?: Tool[];
|
|
21
18
|
}
|
|
22
|
-
export declare function BuddyAssistant({
|
|
19
|
+
export declare function BuddyAssistant({ systemPrompt, autoStartConversation, circleSize, chatPlaceholder, bottomAction, className, voiceSpeed, tools, }: BuddyAssistantProps): JSX.Element;
|
|
@@ -9,9 +9,11 @@ import { HiMiniSpeakerWave, HiMiniSpeakerXMark } from 'react-icons/hi2';
|
|
|
9
9
|
import { BiSolidRightArrow } from 'react-icons/bi';
|
|
10
10
|
let idCounter = 0;
|
|
11
11
|
const genId = () => `ba-${++idCounter}`;
|
|
12
|
-
export function BuddyAssistant({
|
|
12
|
+
export function BuddyAssistant({ systemPrompt, autoStartConversation, circleSize = '160px', chatPlaceholder, bottomAction, className, voiceSpeed = 1, tools, }) {
|
|
13
|
+
var _a;
|
|
13
14
|
const { ai, event, plugin } = useRimori();
|
|
14
15
|
const { isDark } = useTheme(plugin.theme);
|
|
16
|
+
const buddy = (_a = plugin.getUserInfo()) === null || _a === void 0 ? void 0 : _a.study_buddy;
|
|
15
17
|
const [ttsEnabled, setTtsEnabled] = useState(true);
|
|
16
18
|
const [chatInput, setChatInput] = useState('');
|
|
17
19
|
const [messages, setMessages] = useState([]);
|
|
@@ -21,9 +23,10 @@ export function BuddyAssistant({ buddyName, avatarImageUrl, voiceId, systemPromp
|
|
|
21
23
|
useEffect(() => {
|
|
22
24
|
ttsEnabledRef.current = ttsEnabled;
|
|
23
25
|
}, [ttsEnabled]);
|
|
24
|
-
const sender = useMemo(() => new MessageSender((...args) => ai.getVoice(...args), voiceId), [voiceId, ai]);
|
|
26
|
+
const sender = useMemo(() => { var _a; return new MessageSender((...args) => ai.getVoice(...args), (_a = buddy === null || buddy === void 0 ? void 0 : buddy.voiceId) !== null && _a !== void 0 ? _a : ''); }, [buddy === null || buddy === void 0 ? void 0 : buddy.voiceId, ai]);
|
|
25
27
|
// Setup sender callbacks and cleanup
|
|
26
28
|
useEffect(() => {
|
|
29
|
+
sender.setVoiceSpeed(voiceSpeed);
|
|
27
30
|
sender.setOnLoudnessChange((value) => event.emit('self.avatar.triggerLoudness', { loudness: value }));
|
|
28
31
|
sender.setOnEndOfSpeech(() => setIsSpeaking(false));
|
|
29
32
|
return () => sender.cleanup();
|
|
@@ -73,6 +76,8 @@ export function BuddyAssistant({ buddyName, avatarImageUrl, voiceId, systemPromp
|
|
|
73
76
|
}
|
|
74
77
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
75
78
|
}, []);
|
|
79
|
+
if (!buddy)
|
|
80
|
+
return _jsx("div", {});
|
|
76
81
|
const sendMessage = (text) => {
|
|
77
82
|
if (!text.trim() || isLoading)
|
|
78
83
|
return;
|
|
@@ -89,13 +94,13 @@ export function BuddyAssistant({ buddyName, avatarImageUrl, voiceId, systemPromp
|
|
|
89
94
|
setTtsEnabled((prev) => !prev);
|
|
90
95
|
};
|
|
91
96
|
const lastAssistantMessage = [...messages].filter((m) => m.role === 'assistant').pop();
|
|
92
|
-
return (_jsxs("div", { className: `flex flex-col items-center ${className || ''}`, children: [_jsx(CircleAudioAvatar, { width: circleSize, imageUrl:
|
|
97
|
+
return (_jsxs("div", { className: `flex flex-col items-center ${className || ''}`, children: [_jsx(CircleAudioAvatar, { width: circleSize, imageUrl: buddy.avatarUrl, isDarkTheme: isDark, className: "mx-auto" }), _jsxs("div", { className: "flex items-center gap-2 pl-10", children: [_jsx("span", { className: "text-3xl font-semibold", children: buddy.name }), _jsx("button", { type: "button", onClick: handleToggleTts, className: "p-1 rounded-md hover:bg-gray-700/50 transition-colors", title: ttsEnabled ? 'Disable voice' : 'Enable voice', children: ttsEnabled ? (_jsx(HiMiniSpeakerWave, { className: `w-5 h-5 mt-0.5 ${isSpeaking ? 'text-blue-400' : 'text-gray-300'}` })) : (_jsx(HiMiniSpeakerXMark, { className: "w-5 h-5 mt-0.5 text-gray-500" })) })] }), !ttsEnabled && (_jsx("div", { className: "w-full max-w-md rounded-xl bg-gray-800/70 px-4 py-3 text-sm text-gray-200 leading-relaxed border border-gray-700/40 mt-4", children: !(lastAssistantMessage === null || lastAssistantMessage === void 0 ? void 0 : lastAssistantMessage.content) && isLoading ? (_jsxs("span", { className: "inline-flex gap-1 py-0.5", children: [_jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-gray-400 animate-bounce" }), _jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-gray-400 animate-bounce", style: { animationDelay: '0.15s' } }), _jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-gray-400 animate-bounce", style: { animationDelay: '0.3s' } })] })) : (_jsx("span", { className: "whitespace-pre-wrap", children: lastAssistantMessage === null || lastAssistantMessage === void 0 ? void 0 : lastAssistantMessage.content })) })), _jsxs("div", { className: "w-full max-w-md relative mt-4", children: [_jsx("input", { value: chatInput, onChange: (e) => setChatInput(e.target.value), onKeyDown: (e) => {
|
|
93
98
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
94
99
|
e.preventDefault();
|
|
95
100
|
sendMessage(chatInput);
|
|
96
101
|
setChatInput('');
|
|
97
102
|
}
|
|
98
|
-
}, placeholder: chatPlaceholder !== null && chatPlaceholder !== void 0 ? chatPlaceholder : `Ask ${
|
|
103
|
+
}, placeholder: chatPlaceholder !== null && chatPlaceholder !== void 0 ? chatPlaceholder : `Ask ${buddy.name} a question…`, disabled: isLoading, className: "w-full bg-gray-800/50 border border-gray-700 rounded-lg px-3 py-2 pr-16 text-sm text-gray-200 placeholder:text-gray-500 focus:outline-none focus:ring-1 focus:ring-blue-500 disabled:opacity-60" }), _jsxs("div", { className: "absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-1", children: [_jsx(VoiceRecorder, { iconSize: "14", className: "p-1 text-gray-400 hover:text-white transition-colors", disabled: isLoading, onVoiceRecorded: (text) => sendMessage(text), onRecordingStatusChange: () => { } }), _jsx("div", { className: "w-px h-3.5 bg-gray-600" }), _jsx("button", { type: "button", onClick: () => {
|
|
99
104
|
sendMessage(chatInput);
|
|
100
105
|
setChatInput('');
|
|
101
106
|
}, disabled: isLoading || !chatInput.trim(), className: "p-1 text-gray-400 hover:text-white disabled:opacity-40 transition-colors", children: _jsx(BiSolidRightArrow, { className: "w-4 h-4" }) })] })] }), bottomAction && _jsx("div", { className: "w-full max-w-md border-t border-gray-700/60 pt-3", children: bottomAction })] }));
|
package/package.json
CHANGED
|
@@ -17,9 +17,6 @@ export interface BuddyAssistantAutoStart {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export interface BuddyAssistantProps {
|
|
20
|
-
buddyName: string;
|
|
21
|
-
avatarImageUrl: string;
|
|
22
|
-
voiceId: string;
|
|
23
20
|
systemPrompt: string;
|
|
24
21
|
autoStartConversation?: BuddyAssistantAutoStart;
|
|
25
22
|
circleSize?: string;
|
|
@@ -34,9 +31,6 @@ let idCounter = 0;
|
|
|
34
31
|
const genId = () => `ba-${++idCounter}`;
|
|
35
32
|
|
|
36
33
|
export function BuddyAssistant({
|
|
37
|
-
buddyName,
|
|
38
|
-
avatarImageUrl,
|
|
39
|
-
voiceId,
|
|
40
34
|
systemPrompt,
|
|
41
35
|
autoStartConversation,
|
|
42
36
|
circleSize = '160px',
|
|
@@ -45,9 +39,10 @@ export function BuddyAssistant({
|
|
|
45
39
|
className,
|
|
46
40
|
voiceSpeed = 1,
|
|
47
41
|
tools,
|
|
48
|
-
}: BuddyAssistantProps) {
|
|
42
|
+
}: BuddyAssistantProps): JSX.Element {
|
|
49
43
|
const { ai, event, plugin } = useRimori();
|
|
50
44
|
const { isDark } = useTheme(plugin.theme);
|
|
45
|
+
const buddy = plugin.getUserInfo()?.study_buddy;
|
|
51
46
|
|
|
52
47
|
const [ttsEnabled, setTtsEnabled] = useState(true);
|
|
53
48
|
const [chatInput, setChatInput] = useState('');
|
|
@@ -60,10 +55,14 @@ export function BuddyAssistant({
|
|
|
60
55
|
ttsEnabledRef.current = ttsEnabled;
|
|
61
56
|
}, [ttsEnabled]);
|
|
62
57
|
|
|
63
|
-
const sender = useMemo(
|
|
58
|
+
const sender = useMemo(
|
|
59
|
+
() => new MessageSender((...args) => ai.getVoice(...args), buddy?.voiceId ?? ''),
|
|
60
|
+
[buddy?.voiceId, ai],
|
|
61
|
+
);
|
|
64
62
|
|
|
65
63
|
// Setup sender callbacks and cleanup
|
|
66
64
|
useEffect(() => {
|
|
65
|
+
sender.setVoiceSpeed(voiceSpeed);
|
|
67
66
|
sender.setOnLoudnessChange((value: number) => event.emit('self.avatar.triggerLoudness', { loudness: value }));
|
|
68
67
|
sender.setOnEndOfSpeech(() => setIsSpeaking(false));
|
|
69
68
|
return () => sender.cleanup();
|
|
@@ -119,6 +118,8 @@ export function BuddyAssistant({
|
|
|
119
118
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
120
119
|
}, []);
|
|
121
120
|
|
|
121
|
+
if (!buddy) return <div />;
|
|
122
|
+
|
|
122
123
|
const sendMessage = (text: string) => {
|
|
123
124
|
if (!text.trim() || isLoading) return;
|
|
124
125
|
const userMsg: ChatMessage = { id: genId(), role: 'user', content: text };
|
|
@@ -140,11 +141,11 @@ export function BuddyAssistant({
|
|
|
140
141
|
return (
|
|
141
142
|
<div className={`flex flex-col items-center ${className || ''}`}>
|
|
142
143
|
{/* Animated circle avatar */}
|
|
143
|
-
<CircleAudioAvatar width={circleSize} imageUrl={
|
|
144
|
+
<CircleAudioAvatar width={circleSize} imageUrl={buddy.avatarUrl} isDarkTheme={isDark} className="mx-auto" />
|
|
144
145
|
|
|
145
146
|
{/* Buddy name + TTS toggle */}
|
|
146
|
-
<div className="flex items-center gap-2">
|
|
147
|
-
<span className="text-3xl font-semibold">{
|
|
147
|
+
<div className="flex items-center gap-2 pl-10">
|
|
148
|
+
<span className="text-3xl font-semibold">{buddy.name}</span>
|
|
148
149
|
<button
|
|
149
150
|
type="button"
|
|
150
151
|
onClick={handleToggleTts}
|
|
@@ -192,7 +193,7 @@ export function BuddyAssistant({
|
|
|
192
193
|
setChatInput('');
|
|
193
194
|
}
|
|
194
195
|
}}
|
|
195
|
-
placeholder={chatPlaceholder ?? `Ask ${
|
|
196
|
+
placeholder={chatPlaceholder ?? `Ask ${buddy.name} a question…`}
|
|
196
197
|
disabled={isLoading}
|
|
197
198
|
className="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-3 py-2 pr-16 text-sm text-gray-200 placeholder:text-gray-500 focus:outline-none focus:ring-1 focus:ring-blue-500 disabled:opacity-60"
|
|
198
199
|
/>
|