@rimori/react-client 0.4.16 → 0.4.17-next.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.
|
@@ -17,5 +17,7 @@ export interface BuddyAssistantProps {
|
|
|
17
17
|
tools?: Tool[];
|
|
18
18
|
/** Set to true to disable automatic dialect from userInfo. Default: false (dialect enabled). */
|
|
19
19
|
disableDialect?: boolean;
|
|
20
|
+
/** Show the buddy name below the avatar. Default: false. */
|
|
21
|
+
showName?: boolean;
|
|
20
22
|
}
|
|
21
|
-
export declare function BuddyAssistant({ systemPrompt, autoStartConversation, circleSize, chatPlaceholder, bottomAction, className, voiceSpeed, tools, disableDialect, }: BuddyAssistantProps): JSX.Element;
|
|
23
|
+
export declare function BuddyAssistant({ systemPrompt, autoStartConversation, circleSize, chatPlaceholder, bottomAction, className, voiceSpeed, tools, disableDialect, showName, }: BuddyAssistantProps): JSX.Element;
|
|
@@ -5,21 +5,19 @@ import { VoiceRecorder } from './EmbeddedAssistent/VoiceRecorder';
|
|
|
5
5
|
import { MessageSender } from '@rimori/client';
|
|
6
6
|
import { useRimori } from '../../providers/PluginProvider';
|
|
7
7
|
import { useTheme } from '../../hooks/ThemeSetter';
|
|
8
|
-
import { HiMiniSpeakerWave, HiMiniSpeakerXMark } from 'react-icons/hi2';
|
|
9
8
|
import { BiSolidRightArrow } from 'react-icons/bi';
|
|
10
9
|
let idCounter = 0;
|
|
11
10
|
const genId = () => `ba-${++idCounter}`;
|
|
12
|
-
export function BuddyAssistant({ systemPrompt, autoStartConversation, circleSize = '160px', chatPlaceholder, bottomAction, className, voiceSpeed = 1, tools, disableDialect = false, }) {
|
|
13
|
-
var _a;
|
|
11
|
+
export function BuddyAssistant({ systemPrompt, autoStartConversation, circleSize = '160px', chatPlaceholder, bottomAction, className, voiceSpeed = 1, tools, disableDialect = false, showName = false, }) {
|
|
14
12
|
const { ai, event, plugin, userInfo } = useRimori();
|
|
13
|
+
const ttsEnabled = plugin.ttsEnabled;
|
|
15
14
|
const { isDark } = useTheme(plugin.theme);
|
|
16
|
-
const buddy =
|
|
15
|
+
const buddy = userInfo.study_buddy;
|
|
17
16
|
const dialect = !disableDialect ? userInfo === null || userInfo === void 0 ? void 0 : userInfo.dialect : undefined;
|
|
18
17
|
const dialectSystemSuffix = dialect
|
|
19
18
|
? `\n\nThe user is learning the regional ${dialect} dialect. Occasionally use typical regional vocabulary and expressions from this dialect to help them learn local language naturally.`
|
|
20
19
|
: '';
|
|
21
20
|
const dialectTtsInstruction = dialect ? `Speak with a ${dialect} accent and pronunciation.` : undefined;
|
|
22
|
-
const [ttsEnabled, setTtsEnabled] = useState(true);
|
|
23
21
|
const [chatInput, setChatInput] = useState('');
|
|
24
22
|
const [messages, setMessages] = useState([]);
|
|
25
23
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -27,6 +25,11 @@ export function BuddyAssistant({ systemPrompt, autoStartConversation, circleSize
|
|
|
27
25
|
const ttsEnabledRef = useRef(ttsEnabled);
|
|
28
26
|
useEffect(() => {
|
|
29
27
|
ttsEnabledRef.current = ttsEnabled;
|
|
28
|
+
// Stop speaking when TTS is turned off globally
|
|
29
|
+
if (!ttsEnabled && isSpeaking) {
|
|
30
|
+
sender.stop();
|
|
31
|
+
setIsSpeaking(false);
|
|
32
|
+
}
|
|
30
33
|
}, [ttsEnabled]);
|
|
31
34
|
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]);
|
|
32
35
|
useEffect(() => {
|
|
@@ -94,21 +97,14 @@ export function BuddyAssistant({ systemPrompt, autoStartConversation, circleSize
|
|
|
94
97
|
setMessages(newMessages);
|
|
95
98
|
triggerAI(newMessages);
|
|
96
99
|
};
|
|
97
|
-
const handleToggleTts = () => {
|
|
98
|
-
if (ttsEnabled && isSpeaking) {
|
|
99
|
-
sender.stop();
|
|
100
|
-
setIsSpeaking(false);
|
|
101
|
-
}
|
|
102
|
-
setTtsEnabled((prev) => !prev);
|
|
103
|
-
};
|
|
104
100
|
const lastAssistantMessage = [...messages].filter((m) => m.role === 'assistant').pop();
|
|
105
|
-
return (_jsxs("div", { className: `flex flex-col items-center ${className || ''}`, children: [_jsx(CircleAudioAvatar, { width: circleSize, imageUrl: buddy.avatarUrl, isDarkTheme: isDark, className: "mx-auto" }),
|
|
101
|
+
return (_jsxs("div", { className: `flex flex-col items-center ${className || ''}`, children: [_jsx(CircleAudioAvatar, { width: circleSize, imageUrl: buddy.avatarUrl, isDarkTheme: isDark, className: "mx-auto" }), showName && (_jsx("div", { className: "flex items-center gap-2", children: _jsx("span", { className: "text-3xl font-semibold", children: buddy.name }) })), !ttsEnabled ? (_jsx("div", { className: "w-full rounded-xl bg-gray-200/70 dark:bg-gray-800/70 px-4 py-3 text-sm text-gray-800 dark:text-gray-200 leading-relaxed border border-gray-300/40 dark: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 })) })) : null, _jsxs("div", { className: 'w-full relative mt-4 ' + (ttsEnabled ? 'max-w-md' : ''), children: [_jsx("input", { value: chatInput, onChange: (e) => setChatInput(e.target.value), onKeyDown: (e) => {
|
|
106
102
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
107
103
|
e.preventDefault();
|
|
108
104
|
sendMessage(chatInput);
|
|
109
105
|
setChatInput('');
|
|
110
106
|
}
|
|
111
|
-
}, 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("
|
|
107
|
+
}, 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("button", { type: "button", onClick: () => {
|
|
112
108
|
sendMessage(chatInput);
|
|
113
109
|
setChatInput('');
|
|
114
110
|
}, 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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rimori/react-client",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.17-next.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "tsc && sass src/style.scss:dist/style.css",
|
|
21
|
-
"dev": "
|
|
21
|
+
"dev": "tsc -w --preserveWatchOutput",
|
|
22
22
|
"dev:watch": "tsc -w --preserveWatchOutput",
|
|
23
23
|
"css-dev": "sass --watch src/style.scss:dist/style.css",
|
|
24
24
|
"lint": "eslint . --fix",
|
|
25
25
|
"format": "prettier --write ."
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"@rimori/client": "
|
|
28
|
+
"@rimori/client": "2.5.28-next.5",
|
|
29
29
|
"react": "^18.1.0",
|
|
30
30
|
"react-dom": "^18.1.0"
|
|
31
31
|
},
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@eslint/js": "^9.37.0",
|
|
52
|
-
"@rimori/client": "
|
|
52
|
+
"@rimori/client": "2.5.28-next.5",
|
|
53
53
|
"@types/react": "^18.3.21",
|
|
54
54
|
"eslint-config-prettier": "^10.1.8",
|
|
55
55
|
"eslint-plugin-prettier": "^5.5.4",
|
|
@@ -4,7 +4,6 @@ import { VoiceRecorder } from './EmbeddedAssistent/VoiceRecorder';
|
|
|
4
4
|
import { MessageSender, Tool } from '@rimori/client';
|
|
5
5
|
import { useRimori } from '../../providers/PluginProvider';
|
|
6
6
|
import { useTheme } from '../../hooks/ThemeSetter';
|
|
7
|
-
import { HiMiniSpeakerWave, HiMiniSpeakerXMark } from 'react-icons/hi2';
|
|
8
7
|
import { BiSolidRightArrow } from 'react-icons/bi';
|
|
9
8
|
|
|
10
9
|
type ChatMessage = { id: string; role: 'user' | 'assistant'; content: string };
|
|
@@ -27,6 +26,8 @@ export interface BuddyAssistantProps {
|
|
|
27
26
|
tools?: Tool[];
|
|
28
27
|
/** Set to true to disable automatic dialect from userInfo. Default: false (dialect enabled). */
|
|
29
28
|
disableDialect?: boolean;
|
|
29
|
+
/** Show the buddy name below the avatar. Default: false. */
|
|
30
|
+
showName?: boolean;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
let idCounter = 0;
|
|
@@ -42,17 +43,17 @@ export function BuddyAssistant({
|
|
|
42
43
|
voiceSpeed = 1,
|
|
43
44
|
tools,
|
|
44
45
|
disableDialect = false,
|
|
46
|
+
showName = false,
|
|
45
47
|
}: BuddyAssistantProps): JSX.Element {
|
|
46
48
|
const { ai, event, plugin, userInfo } = useRimori();
|
|
49
|
+
const ttsEnabled = plugin.ttsEnabled;
|
|
47
50
|
const { isDark } = useTheme(plugin.theme);
|
|
48
|
-
const buddy =
|
|
51
|
+
const buddy = userInfo.study_buddy;
|
|
49
52
|
const dialect = !disableDialect ? userInfo?.dialect : undefined;
|
|
50
53
|
const dialectSystemSuffix = dialect
|
|
51
54
|
? `\n\nThe user is learning the regional ${dialect} dialect. Occasionally use typical regional vocabulary and expressions from this dialect to help them learn local language naturally.`
|
|
52
55
|
: '';
|
|
53
56
|
const dialectTtsInstruction = dialect ? `Speak with a ${dialect} accent and pronunciation.` : undefined;
|
|
54
|
-
|
|
55
|
-
const [ttsEnabled, setTtsEnabled] = useState(true);
|
|
56
57
|
const [chatInput, setChatInput] = useState('');
|
|
57
58
|
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
|
58
59
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -61,6 +62,11 @@ export function BuddyAssistant({
|
|
|
61
62
|
const ttsEnabledRef = useRef(ttsEnabled);
|
|
62
63
|
useEffect(() => {
|
|
63
64
|
ttsEnabledRef.current = ttsEnabled;
|
|
65
|
+
// Stop speaking when TTS is turned off globally
|
|
66
|
+
if (!ttsEnabled && isSpeaking) {
|
|
67
|
+
sender.stop();
|
|
68
|
+
setIsSpeaking(false);
|
|
69
|
+
}
|
|
64
70
|
}, [ttsEnabled]);
|
|
65
71
|
|
|
66
72
|
const sender = useMemo(
|
|
@@ -140,14 +146,6 @@ export function BuddyAssistant({
|
|
|
140
146
|
triggerAI(newMessages);
|
|
141
147
|
};
|
|
142
148
|
|
|
143
|
-
const handleToggleTts = () => {
|
|
144
|
-
if (ttsEnabled && isSpeaking) {
|
|
145
|
-
sender.stop();
|
|
146
|
-
setIsSpeaking(false);
|
|
147
|
-
}
|
|
148
|
-
setTtsEnabled((prev) => !prev);
|
|
149
|
-
};
|
|
150
|
-
|
|
151
149
|
const lastAssistantMessage = [...messages].filter((m) => m.role === 'assistant').pop();
|
|
152
150
|
|
|
153
151
|
return (
|
|
@@ -155,26 +153,15 @@ export function BuddyAssistant({
|
|
|
155
153
|
{/* Animated circle avatar */}
|
|
156
154
|
<CircleAudioAvatar width={circleSize} imageUrl={buddy.avatarUrl} isDarkTheme={isDark} className="mx-auto" />
|
|
157
155
|
|
|
158
|
-
{/* Buddy name
|
|
159
|
-
|
|
160
|
-
<
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
className="p-1 rounded-md hover:bg-gray-700/50 transition-colors"
|
|
165
|
-
title={ttsEnabled ? 'Disable voice' : 'Enable voice'}
|
|
166
|
-
>
|
|
167
|
-
{ttsEnabled ? (
|
|
168
|
-
<HiMiniSpeakerWave className={`w-5 h-5 mt-0.5 ${isSpeaking ? 'text-blue-400' : 'text-gray-300'}`} />
|
|
169
|
-
) : (
|
|
170
|
-
<HiMiniSpeakerXMark className="w-5 h-5 mt-0.5 text-gray-500" />
|
|
171
|
-
)}
|
|
172
|
-
</button>
|
|
173
|
-
</div>
|
|
156
|
+
{/* Buddy name (hidden by default) */}
|
|
157
|
+
{showName && (
|
|
158
|
+
<div className="flex items-center gap-2">
|
|
159
|
+
<span className="text-3xl font-semibold">{buddy.name}</span>
|
|
160
|
+
</div>
|
|
161
|
+
)}
|
|
174
162
|
|
|
175
|
-
{
|
|
176
|
-
|
|
177
|
-
<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">
|
|
163
|
+
{!ttsEnabled ? (
|
|
164
|
+
<div className="w-full rounded-xl bg-gray-200/70 dark:bg-gray-800/70 px-4 py-3 text-sm text-gray-800 dark:text-gray-200 leading-relaxed border border-gray-300/40 dark:border-gray-700/40 mt-4">
|
|
178
165
|
{!lastAssistantMessage?.content && isLoading ? (
|
|
179
166
|
<span className="inline-flex gap-1 py-0.5">
|
|
180
167
|
<span className="w-1.5 h-1.5 rounded-full bg-gray-400 animate-bounce" />
|
|
@@ -191,10 +178,10 @@ export function BuddyAssistant({
|
|
|
191
178
|
<span className="whitespace-pre-wrap">{lastAssistantMessage?.content}</span>
|
|
192
179
|
)}
|
|
193
180
|
</div>
|
|
194
|
-
)}
|
|
181
|
+
) : null}
|
|
195
182
|
|
|
196
183
|
{/* Chat input */}
|
|
197
|
-
<div className=
|
|
184
|
+
<div className={'w-full relative mt-4 ' + (ttsEnabled ? 'max-w-md' : '')}>
|
|
198
185
|
<input
|
|
199
186
|
value={chatInput}
|
|
200
187
|
onChange={(e) => setChatInput(e.target.value)}
|
|
@@ -217,7 +204,7 @@ export function BuddyAssistant({
|
|
|
217
204
|
onVoiceRecorded={(text) => sendMessage(text)}
|
|
218
205
|
onRecordingStatusChange={() => {}}
|
|
219
206
|
/>
|
|
220
|
-
<div className="w-px h-3.5 bg-gray-600" />
|
|
207
|
+
{/* <div className="w-px h-3.5 bg-gray-600" /> */}
|
|
221
208
|
<button
|
|
222
209
|
type="button"
|
|
223
210
|
onClick={() => {
|