Introduce ability to disable editing the UI (useful for hosted demos) (#48)
This commit is contained in:
parent
5b99dae15f
commit
95387d73bc
@ -12,6 +12,7 @@ description: 'LiveKit Agent Playground allows you to test your LiveKit Agent int
|
|||||||
github_link: 'https://github.com/livekit/agents-playground'
|
github_link: 'https://github.com/livekit/agents-playground'
|
||||||
video_fit: 'cover' # 'contain' or 'cover'
|
video_fit: 'cover' # 'contain' or 'cover'
|
||||||
settings:
|
settings:
|
||||||
|
editable: true # Should the user be able to edit settings in-app
|
||||||
theme_color: 'cyan'
|
theme_color: 'cyan'
|
||||||
chat: true # Enable or disable chat feature
|
chat: true # Enable or disable chat feature
|
||||||
outputs:
|
outputs:
|
||||||
|
|||||||
@ -36,12 +36,6 @@ import {
|
|||||||
import { QRCodeSVG } from "qrcode.react";
|
import { QRCodeSVG } from "qrcode.react";
|
||||||
import { ReactNode, useCallback, useEffect, useMemo, useState } from "react";
|
import { ReactNode, useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
export enum PlaygroundOutputs {
|
|
||||||
Video,
|
|
||||||
Audio,
|
|
||||||
Chat,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PlaygroundMeta {
|
export interface PlaygroundMeta {
|
||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string;
|
||||||
@ -66,7 +60,6 @@ export default function Playground({
|
|||||||
const [messages, setMessages] = useState<ChatMessageType[]>([]);
|
const [messages, setMessages] = useState<ChatMessageType[]>([]);
|
||||||
const [transcripts, setTranscripts] = useState<ChatMessageType[]>([]);
|
const [transcripts, setTranscripts] = useState<ChatMessageType[]>([]);
|
||||||
const { localParticipant } = useLocalParticipant();
|
const { localParticipant } = useLocalParticipant();
|
||||||
const [outputs, setOutputs] = useState<PlaygroundOutputs[]>([]);
|
|
||||||
|
|
||||||
const participants = useRemoteParticipants({
|
const participants = useRemoteParticipants({
|
||||||
updateOnlyOn: [RoomEvent.ParticipantMetadataChanged],
|
updateOnlyOn: [RoomEvent.ParticipantMetadataChanged],
|
||||||
@ -371,7 +364,8 @@ export default function Playground({
|
|||||||
config.description,
|
config.description,
|
||||||
config.settings,
|
config.settings,
|
||||||
config.show_qr,
|
config.show_qr,
|
||||||
// metadata,
|
localParticipant,
|
||||||
|
name,
|
||||||
roomState,
|
roomState,
|
||||||
isAgentConnected,
|
isAgentConnected,
|
||||||
agentState,
|
agentState,
|
||||||
@ -383,7 +377,7 @@ export default function Playground({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
let mobileTabs: PlaygroundTab[] = [];
|
let mobileTabs: PlaygroundTab[] = [];
|
||||||
if (outputs?.includes(PlaygroundOutputs.Video)) {
|
if (config.settings.outputs.video) {
|
||||||
mobileTabs.push({
|
mobileTabs.push({
|
||||||
title: "Video",
|
title: "Video",
|
||||||
content: (
|
content: (
|
||||||
@ -397,7 +391,7 @@ export default function Playground({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outputs?.includes(PlaygroundOutputs.Audio)) {
|
if (config.settings.outputs.audio) {
|
||||||
mobileTabs.push({
|
mobileTabs.push({
|
||||||
title: "Audio",
|
title: "Audio",
|
||||||
content: (
|
content: (
|
||||||
@ -411,7 +405,7 @@ export default function Playground({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outputs?.includes(PlaygroundOutputs.Chat)) {
|
if (config.settings.chat) {
|
||||||
mobileTabs.push({
|
mobileTabs.push({
|
||||||
title: "Chat",
|
title: "Chat",
|
||||||
content: chatTileContent,
|
content: chatTileContent,
|
||||||
@ -458,13 +452,12 @@ export default function Playground({
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`flex-col grow basis-1/2 gap-4 h-full hidden lg:${
|
className={`flex-col grow basis-1/2 gap-4 h-full hidden lg:${
|
||||||
!outputs?.includes(PlaygroundOutputs.Audio) &&
|
!config.settings.outputs.audio && !config.settings.outputs.video
|
||||||
!outputs?.includes(PlaygroundOutputs.Video)
|
|
||||||
? "hidden"
|
? "hidden"
|
||||||
: "flex"
|
: "flex"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{outputs?.includes(PlaygroundOutputs.Video) && (
|
{config.settings.outputs.video && (
|
||||||
<PlaygroundTile
|
<PlaygroundTile
|
||||||
title="Video"
|
title="Video"
|
||||||
className="w-full h-full grow"
|
className="w-full h-full grow"
|
||||||
@ -473,7 +466,7 @@ export default function Playground({
|
|||||||
{videoTileContent}
|
{videoTileContent}
|
||||||
</PlaygroundTile>
|
</PlaygroundTile>
|
||||||
)}
|
)}
|
||||||
{outputs?.includes(PlaygroundOutputs.Audio) && (
|
{config.settings.outputs.audio && (
|
||||||
<PlaygroundTile
|
<PlaygroundTile
|
||||||
title="Audio"
|
title="Audio"
|
||||||
className="w-full h-full grow"
|
className="w-full h-full grow"
|
||||||
@ -484,7 +477,7 @@ export default function Playground({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{outputs?.includes(PlaygroundOutputs.Chat) && (
|
{config.settings.chat && (
|
||||||
<PlaygroundTile
|
<PlaygroundTile
|
||||||
title="Chat"
|
title="Chat"
|
||||||
className="h-full grow basis-1/4 hidden lg:flex"
|
className="h-full grow basis-1/4 hidden lg:flex"
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { Button } from "@/components/button/Button";
|
import { Button } from "@/components/button/Button";
|
||||||
import { LoadingSVG } from "@/components/button/LoadingSVG";
|
import { LoadingSVG } from "@/components/button/LoadingSVG";
|
||||||
import { SettingsDropdown } from "@/components/playground/SettingsDropdown";
|
import { SettingsDropdown } from "@/components/playground/SettingsDropdown";
|
||||||
|
import { useConfig } from "@/hooks/useConfig";
|
||||||
import { ConnectionState } from "livekit-client";
|
import { ConnectionState } from "livekit-client";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
@ -23,6 +24,7 @@ export const PlaygroundHeader = ({
|
|||||||
onConnectClicked,
|
onConnectClicked,
|
||||||
connectionState,
|
connectionState,
|
||||||
}: PlaygroundHeader) => {
|
}: PlaygroundHeader) => {
|
||||||
|
const { config } = useConfig();
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`flex gap-4 pt-4 text-${accentColor}-500 justify-between items-center shrink-0`}
|
className={`flex gap-4 pt-4 text-${accentColor}-500 justify-between items-center shrink-0`}
|
||||||
@ -48,7 +50,7 @@ export const PlaygroundHeader = ({
|
|||||||
<GithubSVG />
|
<GithubSVG />
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
<SettingsDropdown />
|
{config.settings.editable && <SettingsDropdown />}
|
||||||
<Button
|
<Button
|
||||||
accentColor={
|
accentColor={
|
||||||
connectionState === ConnectionState.Connected ? "red" : accentColor
|
connectionState === ConnectionState.Connected ? "red" : accentColor
|
||||||
|
|||||||
@ -66,6 +66,9 @@ export const PlaygroundTabbedTile: React.FC<PlaygroundTabbedTileProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const contentPadding = 4;
|
const contentPadding = 4;
|
||||||
const [activeTab, setActiveTab] = useState(initialTab);
|
const [activeTab, setActiveTab] = useState(initialTab);
|
||||||
|
if(activeTab >= tabs.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`flex flex-col h-full border rounded-sm border-gray-800 text-gray-500 bg-${backgroundColor} ${className}`}
|
className={`flex flex-col h-full border rounded-sm border-gray-800 text-gray-500 bg-${backgroundColor} ${className}`}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ export type AppConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type UserSettings = {
|
export type UserSettings = {
|
||||||
|
editable: boolean;
|
||||||
theme_color: string;
|
theme_color: string;
|
||||||
chat: boolean;
|
chat: boolean;
|
||||||
inputs: {
|
inputs: {
|
||||||
@ -36,6 +37,7 @@ const defaultConfig: AppConfig = {
|
|||||||
description: "A playground for testing LiveKit Agents",
|
description: "A playground for testing LiveKit Agents",
|
||||||
video_fit: "cover",
|
video_fit: "cover",
|
||||||
settings: {
|
settings: {
|
||||||
|
editable: true,
|
||||||
theme_color: "cyan",
|
theme_color: "cyan",
|
||||||
chat: true,
|
chat: true,
|
||||||
inputs: {
|
inputs: {
|
||||||
@ -90,8 +92,13 @@ export const ConfigProvider = ({
|
|||||||
if (!window.location.hash) {
|
if (!window.location.hash) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const appConfigFromSettings = appConfig;
|
||||||
|
if (appConfigFromSettings.settings.editable === false) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
const params = new URLSearchParams(window.location.hash.replace("#", ""));
|
const params = new URLSearchParams(window.location.hash.replace("#", ""));
|
||||||
return {
|
return {
|
||||||
|
editable: true,
|
||||||
chat: params.get("chat") === "1",
|
chat: params.get("chat") === "1",
|
||||||
theme_color: params.get("theme_color"),
|
theme_color: params.get("theme_color"),
|
||||||
inputs: {
|
inputs: {
|
||||||
@ -106,15 +113,19 @@ export const ConfigProvider = ({
|
|||||||
ws_url: "",
|
ws_url: "",
|
||||||
token: ""
|
token: ""
|
||||||
} as UserSettings;
|
} as UserSettings;
|
||||||
}, [])
|
}, [appConfig])
|
||||||
|
|
||||||
const getSettingsFromCookies = useCallback(() => {
|
const getSettingsFromCookies = useCallback(() => {
|
||||||
|
const appConfigFromSettings = appConfig;
|
||||||
|
if (appConfigFromSettings.settings.editable === false) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
const jsonSettings = getCookie("lk_settings");
|
const jsonSettings = getCookie("lk_settings");
|
||||||
if (!jsonSettings) {
|
if (!jsonSettings) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return JSON.parse(jsonSettings) as UserSettings;
|
return JSON.parse(jsonSettings) as UserSettings;
|
||||||
}, [])
|
}, [appConfig])
|
||||||
|
|
||||||
const setUrlSettings = useCallback((us: UserSettings) => {
|
const setUrlSettings = useCallback((us: UserSettings) => {
|
||||||
const obj = new URLSearchParams({
|
const obj = new URLSearchParams({
|
||||||
@ -136,6 +147,9 @@ export const ConfigProvider = ({
|
|||||||
|
|
||||||
const getConfig = useCallback(() => {
|
const getConfig = useCallback(() => {
|
||||||
const appConfigFromSettings = appConfig;
|
const appConfigFromSettings = appConfig;
|
||||||
|
if (appConfigFromSettings.settings.editable === false) {
|
||||||
|
return appConfigFromSettings;
|
||||||
|
}
|
||||||
const cookieSettigs = getSettingsFromCookies();
|
const cookieSettigs = getSettingsFromCookies();
|
||||||
const urlSettings = getSettingsFromUrl();
|
const urlSettings = getSettingsFromUrl();
|
||||||
if(!cookieSettigs) {
|
if(!cookieSettigs) {
|
||||||
@ -163,6 +177,10 @@ export const ConfigProvider = ({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const setUserSettings = useCallback((settings: UserSettings) => {
|
const setUserSettings = useCallback((settings: UserSettings) => {
|
||||||
|
const appConfigFromSettings = appConfig;
|
||||||
|
if (appConfigFromSettings.settings.editable === false) {
|
||||||
|
return
|
||||||
|
}
|
||||||
setUrlSettings(settings);
|
setUrlSettings(settings);
|
||||||
setCookieSettings(settings);
|
setCookieSettings(settings);
|
||||||
_setConfig((prev) => {
|
_setConfig((prev) => {
|
||||||
@ -171,7 +189,7 @@ export const ConfigProvider = ({
|
|||||||
settings: settings,
|
settings: settings,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
}, [setCookieSettings, setUrlSettings]);
|
}, [appConfig, setCookieSettings, setUrlSettings]);
|
||||||
|
|
||||||
const [config, _setConfig] = useState<AppConfig>(getConfig());
|
const [config, _setConfig] = useState<AppConfig>(getConfig());
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,7 @@ export const TokenGeneratorProvider = ({
|
|||||||
const [shouldConnect, setShouldConnect] = useState(false);
|
const [shouldConnect, setShouldConnect] = useState(false);
|
||||||
const connect = useCallback(
|
const connect = useCallback(
|
||||||
async (mode: Mode) => {
|
async (mode: Mode) => {
|
||||||
|
console.log("connecting", mode);
|
||||||
if (mode === "cloud") {
|
if (mode === "cloud") {
|
||||||
if (!generateConnectionDetails) {
|
if (!generateConnectionDetails) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user