<@U084APK40NS> i have cloned this porject lk app ...
# ask-ai
w
@millions-hair-24956 i have cloned this porject lk app create --template voice-assistant-frontend. ....now i want a custom frontend for this like having all the funtionality same but just with my own button and having a screen or some view in top 80% of screen and having the control bar in bottom 20% with having my own mic view but with all functionality that i see in voice-assistant-frontend code...... can you send me the code and direcotry structure for this new implementation that you are showing
g
It seems likely that you want to customize the layout of the
voice-assistant-frontend
project by modifying the UI components while retaining the existing functionality. To achieve this, you can start by adjusting the layout in the
page.tsx
file, which is part of the Next.js app structure. You can use CSS to set the top 80% of the screen for your custom view and the bottom 20% for the control bar. Here's a basic example of how you might structure your
page.tsx
file:
Copy code
'use client';
import { LiveKitRoom, RoomAudioRenderer, ControlBar } from '@livekit/components-react';
import '@livekit/components-styles';
import { useEffect, useState } from 'react';

export default function Page() {
  const room = 'quickstart-room';
  const name = 'quickstart-user';
  const [token, setToken] = useState('');

  useEffect(() => {
    (async () => {
      try {
        const resp = await fetch(`/api/token?room=${room}&username=${name}`);
        const data = await resp.json();
        setToken(data.token);
      } catch (e) {
        console.error(e);
      }
    })();
  }, []);

  if (token === '') {
    return <div>Getting token...</div>;
  }

  return (
    <LiveKitRoom
      video={true}
      audio={true}
      token={token}
      serverUrl={process.env.LIVEKIT_URL}
      data-lk-theme="default"
      style={{ height: '100dvh' }}
    >
      <div style={{ height: '80%', backgroundColor: '#f0f0f0' }}>
        {/* Your custom view goes here */}
      </div>
      <div style={{ height: '20%', backgroundColor: '#ccc' }}>
        <ControlBar />
      </div>
      <RoomAudioRenderer />
    </LiveKitRoom>
  );
}
For the directory structure, you can follow the standard Next.js structure, with your components and styles organized in a way that suits your project. You might have a
components
directory for reusable components and a
styles
directory for CSS files. Would you like more detailed guidance on any specific part of this process? Sources: - https://docs.livekit.io/home/egress/custom-template/ 👍 0 button 👎 0 button 💡 Hint: Mention @runllm in the thread for followups.
w
@millions-hair-24956 in this 'use client' import { AnimatePresence, motion } from 'framer-motion' import { LiveKitRoom, useVoiceAssistant, BarVisualizer, RoomAudioRenderer, VoiceAssistantControlBar, AgentState, DisconnectButton, } from '@livekit/components-react' import { useCallback, useEffect, useState } from 'react' import { MediaDeviceFailure } from 'livekit-client' import type { ConnectionDetails } from './api/connection-details/route' import { NoAgentNotification } from '@/components/NoAgentNotification' import { CloseIcon } from '@/components/CloseIcon' import { useKrispNoiseFilter } from '@livekit/components-react/krisp' export default function Page() { const [connectionDetails, updateConnectionDetails] = useState< ConnectionDetails | undefined >(undefined) const [agentState, setAgentState] = useState<AgentState>('disconnected') const onConnectButtonClicked = useCallback(async () => { // Generate room connection details, including: // - A random Room name // - A random Participant name // - An Access Token to permit the participant to join the room // - The URL of the LiveKit server to connect to // // In real-world application, you would likely allow the user to specify their // own participant name, and possibly to choose from existing rooms to join. const url = new URL( process.env.NEXT_PUBLIC_CONN_DETAILS_ENDPOINT ?? '/api/connection-details', window.location.origin, ) const response = await fetch(url.toString()) const connectionDetailsData = await response.json() updateConnectionDetails(connectionDetailsData) }, []) return ( <main data-lk-theme="default" className="h-full grid content-center bg-[var(--lk-bg)]" > <LiveKitRoom token={connectionDetails?.participantToken} serverUrl={connectionDetails?.serverUrl} connect={connectionDetails !== undefined} audio={true} video={true} onMediaDeviceFailure={onDeviceFailure} onDisconnected={() => { updateConnectionDetails(undefined) }} className="grid grid-rows-[2fr_1fr] items-center" > <SimpleVoiceAssistant onStateChange={setAgentState} /> <ControlBar onConnectButtonClicked={onConnectButtonClicked} agentState={agentState} /> <RoomAudioRenderer /> <NoAgentNotification state={agentState} /> </LiveKitRoom> </main> ) } function SimpleVoiceAssistant(props: { onStateChange: (state: AgentState) => void }) { const { state, audioTrack } = useVoiceAssistant() useEffect(() => { props.onStateChange(state) }, [props, state]) return ( <div className="h-[300px] max-w-[90vw] mx-auto"> <BarVisualizer state={state} barCount={5} trackRef={audioTrack} className="agent-visualizer" options={{ minHeight: 24 }} /> </div> ) } function ControlBar(props: { onConnectButtonClicked: () => void agentState: AgentState }) { /** * Use Krisp background noise reduction when available. * Note: This is only available on Scale plan, see {@link https://livekit.io/pricing | LiveKit Pricing} for more details. */ const krisp = useKrispNoiseFilter() useEffect(() => { krisp.setNoiseFilterEnabled(true) }, []) return ( <div className="relative h-[100px]"> <AnimatePresence> {props.agentState === 'disconnected' && ( <button onClick={() => props.onConnectButtonClicked()}> Start a conversation </button> )} </AnimatePresence> <AnimatePresence> {props.agentState !== 'disconnected' && props.agentState !== 'connecting' && ( <motion.div initial={{ opacity: 0, top: '10px' }} animate={{ opacity: 1, top: 0 }} exit={{ opacity: 0, top: '-10px' }} transition={{ duration: 0.4, ease: [0.09, 1.04, 0.245, 1.055] }} className="flex h-8 absolute left-1/2 -translate-x-1/2 justify-center" > <VoiceAssistantControlBar controls={{ leave: false }} /> <DisconnectButton> <CloseIcon /> </DisconnectButton> </motion.div> )} </AnimatePresence> </div> ) } function onDeviceFailure(error?: MediaDeviceFailure) { console.error(error) alert( 'Error acquiring camera or microphone permissions. Please make sure you grant the necessary permissions in your browser and reload the tab', ) }. In this ciode i want to repl;ace the discoonnect button with my own button which does the disconneting and manages everything else that curently in the code so that this disconneting button jels in well
g
To replace the
DisconnectButton
with your own custom button while maintaining the existing functionality, you can create a custom button component that calls the
disconnect
method on the room context. Here's how you can do it: 1. Create a Custom Disconnect Button: Define a new component that uses the
useRoomContext
hook to access the room instance and call the
disconnect
method. 2. Replace the Existing Disconnect Button: Use your custom button in place of the existing
DisconnectButton
in the
ControlBar
component. Here's an example of how you can implement this:
Copy code
import { useRoomContext } from '@livekit/components-react';
import { CloseIcon } from '@/components/CloseIcon';

function CustomDisconnectButton() {
  const room = useRoomContext();

  const handleDisconnect = () => {
    room.disconnect();
  };

  return (
    <button onClick={handleDisconnect} className="custom-disconnect-button">
      <CloseIcon />
      Disconnect
    </button>
  );
}

function ControlBar(props: {
  onConnectButtonClicked: () => void;
  agentState: AgentState;
}) {
  const krisp = useKrispNoiseFilter();
  useEffect(() => {
    krisp.setNoiseFilterEnabled(true);
  }, []);

  return (
    <div className="relative h-[100px]">
      <AnimatePresence>
        {props.agentState === 'disconnected' && (
          <button onClick={() => props.onConnectButtonClicked()}>
            Start a conversation
          </button>
        )}
      </AnimatePresence>
      <AnimatePresence>
        {props.agentState !== 'disconnected' && props.agentState !== 'connecting' && (
          <motion.div
            initial={{ opacity: 0, top: '10px' }}
            animate={{ opacity: 1, top: 0 }}
            exit={{ opacity: 0, top: '-10px' }}
            transition={{ duration: 0.4, ease: [0.09, 1.04, 0.245, 1.055] }}
            className="flex h-8 absolute left-1/2 -translate-x-1/2 justify-center"
          >
            <VoiceAssistantControlBar controls={{ leave: false }} />
            <CustomDisconnectButton />
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  );
}
In this example,
CustomDisconnectButton
uses the
useRoomContext
hook to access the room instance and calls
room.disconnect()
when clicked. This replaces the existing
DisconnectButton
while maintaining the same functionality. Would you like more detailed guidance on any specific part of this process? Sources: - useDisconnectButton 👍 0 button 👎 0 button 💡 Hint: Mention @runllm in the thread for followups.
w
how to get this useRoomContext @millions-hair-24956
g
To use the
useRoomContext
hook, you need to ensure that your component is nested within a
LiveKitRoom
component, as this component provides the necessary context. The
useRoomContext
hook is part of the
@livekit/components-react
package and allows you to access the
Room
instance within your React components. Here's how you can import and use the
useRoomContext
hook: 1. Import the Hook: First, import the
useRoomContext
from the
@livekit/components-react
package. 2. Use the Hook in Your Component: Call
useRoomContext
within your component to access the
Room
instance. Here's an example:
Copy code
import { useRoomContext } from '@livekit/components-react';

function CustomComponent() {
  const room = useRoomContext();

  const handleDisconnect = () => {
    room.disconnect();
  };

  return (
    <button onClick={handleDisconnect}>
      Disconnect
    </button>
  );
}
Make sure that
CustomComponent
is used within a
LiveKitRoom
component in your component tree, as shown below:
Copy code
<LiveKitRoom
  token={yourToken}
  serverUrl={yourServerUrl}
  connect={true}
>
  <CustomComponent />
</LiveKitRoom>
This setup ensures that
useRoomContext
can access the
Room
instance provided by
LiveKitRoom
. Would you like more detailed guidance on any specific part of this process? Sources: - https://livekit-users.slack.com/archives/C07FY8WHGPM/p1731444545.555719 - https://docs.livekit.io/reference/components/react/concepts/contexts/ 👍 0 button 👎 0 button 💡 Hint: Mention @runllm in the thread for followups.
w
how can i use this already exiting tool in livkit component-react import { setupDisconnectButton } from '@livekit/components-core'; import { ConnectionState } from 'livekit-client'; import * as React from 'react'; import type { DisconnectButtonProps } from '../components'; import { useRoomContext } from '../context'; import { mergeProps } from '../mergeProps'; import { useConnectionState } from './useConnectionStatus'; /** * The
useDisconnectButton
hook is used to implement the
DisconnectButton
or your * custom implementation of it. It adds onClick handler to the button to disconnect * from the room. * * @example *
Copy code
tsx
 * const { buttonProps } = useDisconnectButton(buttonProps);
 * return <button {...buttonProps}>Disconnect</button>;
 *
* @public */ export function useDisconnectButton(props: DisconnectButtonProps) { const room = useRoomContext(); const connectionState = useConnectionState(room); const buttonProps = React.useMemo(() => { const { className, disconnect } = setupDisconnectButton(room); const mergedProps = mergeProps(props, { className, onClick: () => disconnect(props.stopTracks ?? true), disabled: connectionState === ConnectionState.Disconnected, }); return mergedProps; }, [room, props, connectionState]); return { buttonProps }; }. tell me how can use this in function CustomDisconnectButton() { const room = useDisconnectButton() const handleDisconnect = () => { room.disconnect() } return ( <button onClick={handleDisconnect} className="custom-disconnect-button"> <CloseIcon /> Disconnect </button> ) } this code some where correctly @millions-hair-24956
g
If you're looking for further help, please follow-up on one of the other channels available for community support!