@rimori/react-client 0.4.17 → 0.4.18-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.
- package/dist/components/ai/Avatar.d.ts +5 -4
- package/dist/components/ai/Avatar.js +9 -4
- package/dist/components/ai/BuddyAssistant.d.ts +7 -4
- package/dist/components/ai/BuddyAssistant.js +36 -39
- package/dist/components/ai/utils.d.ts +1 -4
- package/dist/components/ai/utils.js +5 -9
- package/dist/components/editor/MarkdownEditor.js +5 -10
- package/dist/hooks/ThemeSetter.d.ts +2 -1
- package/dist/hooks/ThemeSetter.js +5 -2
- package/dist/hooks/UseChatHook.d.ts +0 -2
- package/dist/hooks/UseChatHook.js +16 -11
- package/dist/providers/PluginProvider.d.ts +4 -0
- package/dist/providers/PluginProvider.js +19 -12
- package/package.json +3 -3
- package/src/components/ai/Avatar.tsx +14 -9
- package/src/components/ai/BuddyAssistant.tsx +38 -53
- package/src/components/ai/utils.ts +5 -11
- package/src/components/editor/MarkdownEditor.tsx +5 -11
- package/src/hooks/ThemeSetter.ts +9 -2
- package/src/hooks/UseChatHook.ts +5 -11
- package/src/providers/PluginProvider.tsx +27 -12
- package/README copy.md +0 -1216
|
@@ -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 };
|
|
@@ -17,7 +16,10 @@ export interface BuddyAssistantAutoStart {
|
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
export interface BuddyAssistantProps {
|
|
20
|
-
|
|
19
|
+
/** Server-side prompt name (e.g. 'studyplan.goalSummary'). The backend resolves the system prompt. */
|
|
20
|
+
prompt: string;
|
|
21
|
+
/** Variables for the server-side prompt template. */
|
|
22
|
+
promptVariables?: Record<string, any>;
|
|
21
23
|
autoStartConversation?: BuddyAssistantAutoStart;
|
|
22
24
|
circleSize?: string;
|
|
23
25
|
chatPlaceholder?: string;
|
|
@@ -25,15 +27,16 @@ export interface BuddyAssistantProps {
|
|
|
25
27
|
className?: string;
|
|
26
28
|
voiceSpeed?: number;
|
|
27
29
|
tools?: Tool[];
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
+
/** Show the buddy name below the avatar. Default: false. */
|
|
31
|
+
showName?: boolean;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
let idCounter = 0;
|
|
33
35
|
const genId = () => `ba-${++idCounter}`;
|
|
34
36
|
|
|
35
37
|
export function BuddyAssistant({
|
|
36
|
-
|
|
38
|
+
prompt,
|
|
39
|
+
promptVariables,
|
|
37
40
|
autoStartConversation,
|
|
38
41
|
circleSize = '160px',
|
|
39
42
|
chatPlaceholder,
|
|
@@ -41,18 +44,14 @@ export function BuddyAssistant({
|
|
|
41
44
|
className,
|
|
42
45
|
voiceSpeed = 1,
|
|
43
46
|
tools,
|
|
44
|
-
|
|
47
|
+
showName = false,
|
|
45
48
|
}: BuddyAssistantProps): JSX.Element {
|
|
46
49
|
const { ai, event, plugin, userInfo } = useRimori();
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
? `\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
|
-
: '';
|
|
50
|
+
const ttsEnabled = plugin.ttsEnabled;
|
|
51
|
+
const { isDark } = useTheme(plugin.theme, true);
|
|
52
|
+
const buddy = userInfo.study_buddy;
|
|
53
|
+
const dialect = userInfo?.dialect;
|
|
53
54
|
const dialectTtsInstruction = dialect ? `Speak with a ${dialect} accent and pronunciation.` : undefined;
|
|
54
|
-
|
|
55
|
-
const [ttsEnabled, setTtsEnabled] = useState(true);
|
|
56
55
|
const [chatInput, setChatInput] = useState('');
|
|
57
56
|
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
|
58
57
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -61,6 +60,11 @@ export function BuddyAssistant({
|
|
|
61
60
|
const ttsEnabledRef = useRef(ttsEnabled);
|
|
62
61
|
useEffect(() => {
|
|
63
62
|
ttsEnabledRef.current = ttsEnabled;
|
|
63
|
+
// Stop speaking when TTS is turned off globally
|
|
64
|
+
if (!ttsEnabled && isSpeaking) {
|
|
65
|
+
sender.stop();
|
|
66
|
+
setIsSpeaking(false);
|
|
67
|
+
}
|
|
64
68
|
}, [ttsEnabled]);
|
|
65
69
|
|
|
66
70
|
const sender = useMemo(
|
|
@@ -80,17 +84,15 @@ export function BuddyAssistant({
|
|
|
80
84
|
return () => sender.cleanup();
|
|
81
85
|
}, [sender]);
|
|
82
86
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
...history.map((m) => ({ role: m.role, content: m.content })),
|
|
87
|
-
];
|
|
87
|
+
const buildApiMessages = (history: ChatMessage[]) => {
|
|
88
|
+
return history.map((m) => ({ role: m.role, content: m.content }));
|
|
89
|
+
};
|
|
88
90
|
|
|
89
91
|
const triggerAI = (history: ChatMessage[]) => {
|
|
90
92
|
setIsLoading(true);
|
|
91
|
-
void ai.
|
|
92
|
-
buildApiMessages(history),
|
|
93
|
-
(id: string, partial: string, finished: boolean) => {
|
|
93
|
+
void ai.getStreamedText({
|
|
94
|
+
messages: buildApiMessages(history),
|
|
95
|
+
onMessage: (id: string, partial: string, finished: boolean) => {
|
|
94
96
|
setIsLoading(!finished);
|
|
95
97
|
const assistantId = `ai-${id}`;
|
|
96
98
|
setMessages((prev) => {
|
|
@@ -106,7 +108,9 @@ export function BuddyAssistant({
|
|
|
106
108
|
}
|
|
107
109
|
},
|
|
108
110
|
tools,
|
|
109
|
-
|
|
111
|
+
prompt,
|
|
112
|
+
variables: promptVariables,
|
|
113
|
+
});
|
|
110
114
|
};
|
|
111
115
|
|
|
112
116
|
// Auto-start conversation on mount
|
|
@@ -140,14 +144,6 @@ export function BuddyAssistant({
|
|
|
140
144
|
triggerAI(newMessages);
|
|
141
145
|
};
|
|
142
146
|
|
|
143
|
-
const handleToggleTts = () => {
|
|
144
|
-
if (ttsEnabled && isSpeaking) {
|
|
145
|
-
sender.stop();
|
|
146
|
-
setIsSpeaking(false);
|
|
147
|
-
}
|
|
148
|
-
setTtsEnabled((prev) => !prev);
|
|
149
|
-
};
|
|
150
|
-
|
|
151
147
|
const lastAssistantMessage = [...messages].filter((m) => m.role === 'assistant').pop();
|
|
152
148
|
|
|
153
149
|
return (
|
|
@@ -155,26 +151,15 @@ export function BuddyAssistant({
|
|
|
155
151
|
{/* Animated circle avatar */}
|
|
156
152
|
<CircleAudioAvatar width={circleSize} imageUrl={buddy.avatarUrl} isDarkTheme={isDark} className="mx-auto" />
|
|
157
153
|
|
|
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>
|
|
154
|
+
{/* Buddy name (hidden by default) */}
|
|
155
|
+
{showName && (
|
|
156
|
+
<div className="flex items-center gap-2">
|
|
157
|
+
<span className="text-3xl font-semibold">{buddy.name}</span>
|
|
158
|
+
</div>
|
|
159
|
+
)}
|
|
174
160
|
|
|
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">
|
|
161
|
+
{!plugin.ttsEnabled && messages.length > 1 ? (
|
|
162
|
+
<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
163
|
{!lastAssistantMessage?.content && isLoading ? (
|
|
179
164
|
<span className="inline-flex gap-1 py-0.5">
|
|
180
165
|
<span className="w-1.5 h-1.5 rounded-full bg-gray-400 animate-bounce" />
|
|
@@ -191,10 +176,10 @@ export function BuddyAssistant({
|
|
|
191
176
|
<span className="whitespace-pre-wrap">{lastAssistantMessage?.content}</span>
|
|
192
177
|
)}
|
|
193
178
|
</div>
|
|
194
|
-
)}
|
|
179
|
+
) : null}
|
|
195
180
|
|
|
196
181
|
{/* Chat input */}
|
|
197
|
-
<div className=
|
|
182
|
+
<div className={'w-full relative mt-4 ' + (ttsEnabled ? 'max-w-md' : '')}>
|
|
198
183
|
<input
|
|
199
184
|
value={chatInput}
|
|
200
185
|
onChange={(e) => setChatInput(e.target.value)}
|
|
@@ -217,7 +202,7 @@ export function BuddyAssistant({
|
|
|
217
202
|
onVoiceRecorded={(text) => sendMessage(text)}
|
|
218
203
|
onRecordingStatusChange={() => {}}
|
|
219
204
|
/>
|
|
220
|
-
<div className="w-px h-3.5 bg-gray-600" />
|
|
205
|
+
{/* <div className="w-px h-3.5 bg-gray-600" /> */}
|
|
221
206
|
<button
|
|
222
207
|
type="button"
|
|
223
208
|
onClick={() => {
|
|
@@ -1,22 +1,16 @@
|
|
|
1
|
-
/** @deprecated Use server-side prompt definitions (prompt + variables) instead of building messages client-side. */
|
|
2
1
|
export interface FirstMessages {
|
|
3
|
-
instructions?: string;
|
|
4
2
|
userMessage?: string;
|
|
5
3
|
assistantMessage?: string;
|
|
6
4
|
}
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
export function getFirstMessages(instructions: FirstMessages): any[] {
|
|
6
|
+
export function getFirstMessages(config: FirstMessages): any[] {
|
|
10
7
|
const messages = [];
|
|
11
8
|
|
|
12
|
-
if (
|
|
13
|
-
messages.push({ id: '1', role: '
|
|
9
|
+
if (config.userMessage) {
|
|
10
|
+
messages.push({ id: '1', role: 'user', content: config.userMessage });
|
|
14
11
|
}
|
|
15
|
-
if (
|
|
16
|
-
messages.push({ id: '2', role: '
|
|
17
|
-
}
|
|
18
|
-
if (instructions.assistantMessage) {
|
|
19
|
-
messages.push({ id: '3', role: 'assistant', content: instructions.assistantMessage });
|
|
12
|
+
if (config.assistantMessage) {
|
|
13
|
+
messages.push({ id: '2', role: 'assistant', content: config.assistantMessage });
|
|
20
14
|
}
|
|
21
15
|
|
|
22
16
|
return messages;
|
|
@@ -717,17 +717,11 @@ export const MarkdownEditor = ({
|
|
|
717
717
|
if (from === to) return;
|
|
718
718
|
const selectedText = editor.state.doc.textBetween(from, to, '\n');
|
|
719
719
|
setIsTransforming(true);
|
|
720
|
-
const transformed = await ai.getText(
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
},
|
|
726
|
-
{
|
|
727
|
-
role: 'user',
|
|
728
|
-
content: `Instruction: ${prompt}\n\nText:\n${selectedText}`,
|
|
729
|
-
},
|
|
730
|
-
]);
|
|
720
|
+
const transformed = await ai.getText({
|
|
721
|
+
messages: [],
|
|
722
|
+
prompt: 'global.editor.transform-text',
|
|
723
|
+
variables: { instruction: prompt, text: selectedText },
|
|
724
|
+
});
|
|
731
725
|
setIsTransforming(false);
|
|
732
726
|
if (!transformed) return;
|
|
733
727
|
editor.chain().focus().deleteRange({ from, to }).insertContentAt(from, transformed).run();
|
package/src/hooks/ThemeSetter.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { Theme } from '@rimori/client';
|
|
2
2
|
|
|
3
|
-
export function useTheme(
|
|
3
|
+
export function useTheme(
|
|
4
|
+
theme: Theme = 'system',
|
|
5
|
+
isDisabled = false,
|
|
6
|
+
): { isDark: boolean; theme: Theme; isDisabled: boolean } {
|
|
4
7
|
const isDark = theme === 'system' ? systenUsesDarkMode() : theme === 'dark';
|
|
5
8
|
|
|
9
|
+
if (isDisabled) {
|
|
10
|
+
return { isDark, theme, isDisabled };
|
|
11
|
+
}
|
|
12
|
+
|
|
6
13
|
const dom = document.documentElement;
|
|
7
14
|
dom.dataset.theme = isDark ? 'dark' : 'light';
|
|
8
15
|
dom.style.colorScheme = isDark ? 'dark' : 'light';
|
|
@@ -19,7 +26,7 @@ export function useTheme(theme: Theme = 'system'): { isDark: boolean; theme: The
|
|
|
19
26
|
'bg-fixed',
|
|
20
27
|
);
|
|
21
28
|
|
|
22
|
-
return { isDark, theme };
|
|
29
|
+
return { isDark, theme, isDisabled };
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
function systenUsesDarkMode(): boolean {
|
package/src/hooks/UseChatHook.ts
CHANGED
|
@@ -13,8 +13,6 @@ export interface UseChatConfig {
|
|
|
13
13
|
export function useChat(
|
|
14
14
|
tools?: Tool[],
|
|
15
15
|
options?: {
|
|
16
|
-
/** @deprecated Use uuid variable with resolver 'knowledgeEntry' in prompt definitions instead. */
|
|
17
|
-
knowledgeId?: string;
|
|
18
16
|
/** Server-side prompt name (e.g. 'storytelling.story'). When set, the backend resolves the system prompt. */
|
|
19
17
|
prompt?: string;
|
|
20
18
|
/** Variables for the server-side prompt template. */
|
|
@@ -42,13 +40,12 @@ export function useChat(
|
|
|
42
40
|
const allMessages = [...messages, ...appendMessages];
|
|
43
41
|
setMessages(allMessages);
|
|
44
42
|
setIsLoading(true);
|
|
45
|
-
const knowledgeId = options?.knowledgeId;
|
|
46
43
|
const promptVariables = configRef.current.promptVariables ?? options?.promptVariables;
|
|
47
44
|
const prompt = configRef.current.prompt ?? options?.prompt;
|
|
48
45
|
|
|
49
|
-
void ai.
|
|
50
|
-
allMessages,
|
|
51
|
-
(id, message, finished: boolean, toolInvocations?: ToolInvocation[]) => {
|
|
46
|
+
void ai.getStreamedText({
|
|
47
|
+
messages: allMessages,
|
|
48
|
+
onMessage: (id, message, finished: boolean, toolInvocations?: ToolInvocation[]) => {
|
|
52
49
|
setIsLoading(!finished);
|
|
53
50
|
setMessages((prev) => {
|
|
54
51
|
const last = prev[prev.length - 1];
|
|
@@ -59,12 +56,9 @@ export function useChat(
|
|
|
59
56
|
});
|
|
60
57
|
},
|
|
61
58
|
tools,
|
|
62
|
-
false,
|
|
63
|
-
undefined,
|
|
64
|
-
knowledgeId,
|
|
65
59
|
prompt,
|
|
66
|
-
promptVariables,
|
|
67
|
-
);
|
|
60
|
+
variables: promptVariables,
|
|
61
|
+
});
|
|
68
62
|
};
|
|
69
63
|
|
|
70
64
|
return {
|
|
@@ -9,6 +9,10 @@ import { Theme } from '@rimori/client';
|
|
|
9
9
|
interface PluginProviderProps {
|
|
10
10
|
children: ReactNode;
|
|
11
11
|
pluginId: string;
|
|
12
|
+
/** Pre-constructed RimoriClient (federation mode). When provided, skips the handshake entirely. */
|
|
13
|
+
client?: RimoriClient;
|
|
14
|
+
/** Pre-loaded user info (federation mode). Required when client is provided. */
|
|
15
|
+
userInfo?: UserInfo;
|
|
12
16
|
settings?: {
|
|
13
17
|
disableContextMenu?: boolean;
|
|
14
18
|
};
|
|
@@ -21,14 +25,21 @@ interface PluginContextValue {
|
|
|
21
25
|
|
|
22
26
|
const PluginContext = createContext<PluginContextValue | null>(null);
|
|
23
27
|
|
|
24
|
-
export const PluginProvider: React.FC<PluginProviderProps> = ({
|
|
25
|
-
|
|
28
|
+
export const PluginProvider: React.FC<PluginProviderProps> = ({
|
|
29
|
+
children,
|
|
30
|
+
pluginId,
|
|
31
|
+
client: injectedClient,
|
|
32
|
+
userInfo: injectedUserInfo,
|
|
33
|
+
settings,
|
|
34
|
+
}) => {
|
|
35
|
+
const isFederated = !!injectedClient;
|
|
36
|
+
const [client, setClient] = useState<RimoriClient | null>(injectedClient ?? null);
|
|
26
37
|
const [standaloneClient, setStandaloneClient] = useState<StandaloneClient | boolean>(false);
|
|
27
38
|
const [applicationMode, setApplicationMode] = useState<string | null>(null);
|
|
28
39
|
const [theme, setTheme] = useState<Theme | undefined>(undefined);
|
|
29
|
-
const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
|
|
40
|
+
const [userInfo, setUserInfo] = useState<UserInfo | null>(injectedUserInfo ?? null);
|
|
30
41
|
|
|
31
|
-
useTheme(theme);
|
|
42
|
+
useTheme(theme, isFederated);
|
|
32
43
|
|
|
33
44
|
// Init PostHog once per plugin iframe
|
|
34
45
|
useEffect(() => {
|
|
@@ -51,6 +62,9 @@ export const PluginProvider: React.FC<PluginProviderProps> = ({ children, plugin
|
|
|
51
62
|
const isSettings = applicationMode === 'settings';
|
|
52
63
|
|
|
53
64
|
useEffect(() => {
|
|
65
|
+
// In federation mode the client is already ready — skip handshake
|
|
66
|
+
if (isFederated) return;
|
|
67
|
+
|
|
54
68
|
initEventBus(pluginId);
|
|
55
69
|
|
|
56
70
|
// Check if we're in an iframe context - if not, we're standalone
|
|
@@ -65,7 +79,6 @@ export const PluginProvider: React.FC<PluginProviderProps> = ({ children, plugin
|
|
|
65
79
|
if ((!standaloneDetected && !client) || (standaloneDetected && standaloneClient === true)) {
|
|
66
80
|
void RimoriClient.getInstance(pluginId).then((client) => {
|
|
67
81
|
setClient(client);
|
|
68
|
-
// Set initial userInfo
|
|
69
82
|
setUserInfo(client.plugin.getUserInfo());
|
|
70
83
|
|
|
71
84
|
// Get applicationMode and theme from MessageChannel query params
|
|
@@ -76,7 +89,7 @@ export const PluginProvider: React.FC<PluginProviderProps> = ({ children, plugin
|
|
|
76
89
|
}
|
|
77
90
|
});
|
|
78
91
|
}
|
|
79
|
-
}, [pluginId, standaloneClient, client]);
|
|
92
|
+
}, [pluginId, standaloneClient, client, isFederated]);
|
|
80
93
|
|
|
81
94
|
// Identify user in PostHog when userInfo is available
|
|
82
95
|
useEffect(() => {
|
|
@@ -90,7 +103,7 @@ export const PluginProvider: React.FC<PluginProviderProps> = ({ children, plugin
|
|
|
90
103
|
if (!client) return;
|
|
91
104
|
|
|
92
105
|
const unsubscribe = client.plugin.onRimoriInfoUpdate((info) => {
|
|
93
|
-
console.log('[PluginProvider] Received RimoriInfo update, updating userInfo');
|
|
106
|
+
// console.log('[PluginProvider] Received RimoriInfo update, updating userInfo + ttsEnabled:', info.ttsEnabled);
|
|
94
107
|
setUserInfo(info.profile);
|
|
95
108
|
});
|
|
96
109
|
|
|
@@ -100,6 +113,7 @@ export const PluginProvider: React.FC<PluginProviderProps> = ({ children, plugin
|
|
|
100
113
|
useEffect(() => {
|
|
101
114
|
if (!client) return;
|
|
102
115
|
if (isSidebar) return; //sidebar pages should not report url changes
|
|
116
|
+
if (isFederated) return; // federation mode: host handles URL sync
|
|
103
117
|
|
|
104
118
|
// react router overwrites native pushstate so it gets wrapped to detect url changes
|
|
105
119
|
const originalPushState = history.pushState;
|
|
@@ -108,10 +122,11 @@ export const PluginProvider: React.FC<PluginProviderProps> = ({ children, plugin
|
|
|
108
122
|
client.event.emit('session.triggerUrlChange', { url: location.hash });
|
|
109
123
|
return result;
|
|
110
124
|
};
|
|
111
|
-
}, [client, isSidebar]);
|
|
125
|
+
}, [client, isSidebar, isFederated]);
|
|
112
126
|
|
|
113
127
|
useEffect(() => {
|
|
114
128
|
if (!client) return;
|
|
129
|
+
if (isFederated) return;
|
|
115
130
|
|
|
116
131
|
const checkScrollbar = (): void => {
|
|
117
132
|
const hasScrollbar = document.documentElement.scrollHeight > window.innerHeight;
|
|
@@ -133,7 +148,7 @@ export const PluginProvider: React.FC<PluginProviderProps> = ({ children, plugin
|
|
|
133
148
|
window.removeEventListener('resize', checkScrollbar);
|
|
134
149
|
resizeObserver.disconnect();
|
|
135
150
|
};
|
|
136
|
-
}, [client]);
|
|
151
|
+
}, [client, isFederated]);
|
|
137
152
|
|
|
138
153
|
if (standaloneClient instanceof StandaloneClient) {
|
|
139
154
|
return (
|
|
@@ -162,9 +177,9 @@ export const useRimori = (): RimoriClient & { userInfo: UserInfo } => {
|
|
|
162
177
|
if (context === null) {
|
|
163
178
|
throw new Error('useRimori must be used within an PluginProvider');
|
|
164
179
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
180
|
+
return Object.assign(context.client, {
|
|
181
|
+
userInfo: context.userInfo,
|
|
182
|
+
}) as RimoriClient & { userInfo: UserInfo };
|
|
168
183
|
};
|
|
169
184
|
|
|
170
185
|
function getUrlParam(name: string): string | null {
|