@@ -23,11 +23,13 @@ import {
2323 useRoomInfo ,
2424 useTracks ,
2525 useVoiceAssistant ,
26+ useRoomContext ,
2627} from "@livekit/components-react" ;
2728import { ConnectionState , LocalParticipant , Track } from "livekit-client" ;
2829import { QRCodeSVG } from "qrcode.react" ;
2930import { ReactNode , useCallback , useEffect , useMemo , useState } from "react" ;
3031import tailwindTheme from "../../lib/tailwindTheme.preval" ;
32+ import { EditableNameValueRow } from "@/components/config/NameValueRow" ;
3133
3234export interface PlaygroundMeta {
3335 name : string ;
@@ -56,6 +58,10 @@ export default function Playground({
5658
5759 const roomState = useConnectionState ( ) ;
5860 const tracks = useTracks ( ) ;
61+ const room = useRoomContext ( ) ;
62+
63+ const [ rpcMethod , setRpcMethod ] = useState ( "" ) ;
64+ const [ rpcPayload , setRpcPayload ] = useState ( "" ) ;
5965
6066 useEffect ( ( ) => {
6167 if ( roomState === ConnectionState . Connected ) {
@@ -212,6 +218,21 @@ export default function Playground({
212218 return < > </ > ;
213219 } , [ config . settings . theme_color , voiceAssistant . audioTrack ] ) ;
214220
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+
215236 const settingsTileContent = useMemo ( ( ) => {
216237 return (
217238 < div className = "flex flex-col gap-4 h-full w-full items-start overflow-y-auto" >
@@ -222,19 +243,65 @@ export default function Playground({
222243 ) }
223244
224245 < 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 >
238305 </ ConfigurationPanelItem >
239306 < ConfigurationPanelItem title = "Status" >
240307 < div className = "flex flex-col gap-2" >
@@ -327,6 +394,9 @@ export default function Playground({
327394 themeColors ,
328395 setUserSettings ,
329396 voiceAssistant . agent ,
397+ rpcMethod ,
398+ rpcPayload ,
399+ handleRpcCall ,
330400 ] ) ;
331401
332402 let mobileTabs : PlaygroundTab [ ] = [ ] ;
0 commit comments