@@ -23,11 +23,13 @@ import {
23
23
useRoomInfo ,
24
24
useTracks ,
25
25
useVoiceAssistant ,
26
+ useRoomContext ,
26
27
} from "@livekit/components-react" ;
27
28
import { ConnectionState , LocalParticipant , Track } from "livekit-client" ;
28
29
import { QRCodeSVG } from "qrcode.react" ;
29
30
import { ReactNode , useCallback , useEffect , useMemo , useState } from "react" ;
30
31
import tailwindTheme from "../../lib/tailwindTheme.preval" ;
32
+ import { EditableNameValueRow } from "@/components/config/NameValueRow" ;
31
33
32
34
export interface PlaygroundMeta {
33
35
name : string ;
@@ -56,6 +58,10 @@ export default function Playground({
56
58
57
59
const roomState = useConnectionState ( ) ;
58
60
const tracks = useTracks ( ) ;
61
+ const room = useRoomContext ( ) ;
62
+
63
+ const [ rpcMethod , setRpcMethod ] = useState ( "" ) ;
64
+ const [ rpcPayload , setRpcPayload ] = useState ( "" ) ;
59
65
60
66
useEffect ( ( ) => {
61
67
if ( roomState === ConnectionState . Connected ) {
@@ -212,6 +218,21 @@ export default function Playground({
212
218
return < > </ > ;
213
219
} , [ config . settings . theme_color , voiceAssistant . audioTrack ] ) ;
214
220
221
+ const handleRpcCall = useCallback ( async ( ) => {
222
+ if ( ! voiceAssistant . agent || ! room ) return ;
223
+
224
+ try {
225
+ const response = await room . localParticipant . performRpc ( {
226
+ destinationIdentity : voiceAssistant . agent . identity ,
227
+ method : rpcMethod ,
228
+ payload : rpcPayload ,
229
+ } ) ;
230
+ console . log ( 'RPC response:' , response ) ;
231
+ } catch ( e ) {
232
+ console . error ( 'RPC call failed:' , e ) ;
233
+ }
234
+ } , [ room , rpcMethod , rpcPayload , voiceAssistant . agent ] ) ;
235
+
215
236
const settingsTileContent = useMemo ( ( ) => {
216
237
return (
217
238
< div className = "flex flex-col gap-4 h-full w-full items-start overflow-y-auto" >
@@ -222,19 +243,65 @@ export default function Playground({
222
243
) }
223
244
224
245
< ConfigurationPanelItem title = "Settings" >
225
- { localParticipant && (
226
- < div className = "flex flex-col gap-2" >
227
- < NameValueRow
228
- name = "Room"
229
- value = { name }
230
- valueColor = { `${ config . settings . theme_color } -500` }
231
- />
232
- < NameValueRow
233
- name = "Participant"
234
- value = { localParticipant . identity }
235
- />
236
- </ div >
237
- ) }
246
+ < div className = "flex flex-col gap-4" >
247
+ < EditableNameValueRow
248
+ name = "Room"
249
+ value = { roomState === ConnectionState . Connected ? name : config . settings . room_name }
250
+ valueColor = { `${ config . settings . theme_color } -500` }
251
+ onValueChange = { ( value ) => {
252
+ const newSettings = { ...config . settings } ;
253
+ newSettings . room_name = value ;
254
+ setUserSettings ( newSettings ) ;
255
+ } }
256
+ placeholder = "Enter room name"
257
+ editable = { roomState !== ConnectionState . Connected }
258
+ />
259
+ < EditableNameValueRow
260
+ name = "Participant"
261
+ value = { roomState === ConnectionState . Connected ?
262
+ ( localParticipant ?. identity || '' ) :
263
+ ( config . settings . participant_name || '' ) }
264
+ valueColor = { `${ config . settings . theme_color } -500` }
265
+ onValueChange = { ( value ) => {
266
+ const newSettings = { ...config . settings } ;
267
+ newSettings . participant_name = value ;
268
+ setUserSettings ( newSettings ) ;
269
+ } }
270
+ placeholder = "Enter participant id"
271
+ editable = { roomState !== ConnectionState . Connected }
272
+ />
273
+ </ div >
274
+ < div className = "flex flex-col gap-2 mt-4" >
275
+ < div className = "text-xs text-gray-500 mt-2" > RPC Method</ div >
276
+ < input
277
+ type = "text"
278
+ value = { rpcMethod }
279
+ onChange = { ( e ) => setRpcMethod ( e . target . value ) }
280
+ className = "w-full text-white text-sm bg-transparent border border-gray-800 rounded-sm px-3 py-2"
281
+ placeholder = "RPC method name"
282
+ />
283
+
284
+ < div className = "text-xs text-gray-500 mt-2" > RPC Payload</ div >
285
+ < textarea
286
+ value = { rpcPayload }
287
+ onChange = { ( e ) => setRpcPayload ( e . target . value ) }
288
+ className = "w-full text-white text-sm bg-transparent border border-gray-800 rounded-sm px-3 py-2"
289
+ placeholder = "RPC payload"
290
+ rows = { 2 }
291
+ />
292
+
293
+ < button
294
+ onClick = { handleRpcCall }
295
+ disabled = { ! voiceAssistant . agent || ! rpcMethod }
296
+ className = { `mt-2 px-2 py-1 rounded-sm text-xs
297
+ ${ voiceAssistant . agent && rpcMethod
298
+ ? `bg-${ config . settings . theme_color } -500 hover:bg-${ config . settings . theme_color } -600`
299
+ : 'bg-gray-700 cursor-not-allowed'
300
+ } text-white`}
301
+ >
302
+ Perform RPC Call
303
+ </ button >
304
+ </ div >
238
305
</ ConfigurationPanelItem >
239
306
< ConfigurationPanelItem title = "Status" >
240
307
< div className = "flex flex-col gap-2" >
@@ -327,6 +394,9 @@ export default function Playground({
327
394
themeColors ,
328
395
setUserSettings ,
329
396
voiceAssistant . agent ,
397
+ rpcMethod ,
398
+ rpcPayload ,
399
+ handleRpcCall ,
330
400
] ) ;
331
401
332
402
let mobileTabs : PlaygroundTab [ ] = [ ] ;
0 commit comments