@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({ buddyName, avatarImageUrl, voiceId, systemPrompt, autoStartConversation, circleSize, chatPlaceholder, bottomAction, className, voiceSpeed, tools, }: BuddyAssistantProps): import("react/jsx-runtime").JSX.Element;
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({ buddyName, avatarImageUrl, voiceId, systemPrompt, autoStartConversation, circleSize = '160px', chatPlaceholder, bottomAction, className, voiceSpeed = 1, tools, }) {
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: avatarImageUrl, isDarkTheme: isDark, className: "mx-auto" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-3xl font-semibold", children: buddyName }), _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) => {
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 ${buddyName} 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: () => {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rimori/react-client",
3
- "version": "0.4.11-next.2",
3
+ "version": "0.4.11-next.3",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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(() => new MessageSender((...args) => ai.getVoice(...args), voiceId), [voiceId, ai]);
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={avatarImageUrl} isDarkTheme={isDark} className="mx-auto" />
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">{buddyName}</span>
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 ${buddyName} a question…`}
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
  />